8e71a3a84882f686679a04837e29d70a63db046e
[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_cror_regression(self):
101 """another bad hack!
102 """
103 dis = ["cror 28, 5, 11"]
104 lst = bytes([0x83, 0x5b, 0x75, 0x4f]) # 4f855b83
105 cr = 0x35055058
106 p = Program(lst, bigendian)
107 p.assembly = '\n'.join(dis)+'\n'
108 self.add_case(p, initial_cr=cr)
109
110 def case_mfocrf_regression(self):
111 """bit of a bad hack. comes from microwatt 1.bin instruction 0x106d0
112 as the mask is non-standard, gnu-as barfs. so we fake it up directly
113 from the binary
114 """
115 mask = 0b10000111
116 dis = [f"mfocrf 2, {mask}"]
117 lst = bytes([0x26, 0x78, 0xb8, 0x7c]) # 0x7cb87826
118 cr = 0x5F9E080E
119 p = Program(lst, bigendian)
120 p.assembly = '\n'.join(dis)+'\n'
121 self.add_case(p, initial_cr=cr)
122
123 def case_mtocrf_regression(self):
124 """microwatt 1.bin regression, same hack as above.
125 106b4: 21 d9 96 7d .long 0x7d96d921 # mtocrf 12, 0b01101101
126 """
127 mask = 0b01101101
128 dis = [f"mtocrf 12, {mask}"]
129 lst = bytes([0x21, 0xd9, 0x96, 0x7d]) # 0x7d96d921
130 cr = 0x529e08fe
131 initial_regs = [0] * 32
132 initial_regs[12] = 0xffffffffffffffff
133 p = Program(lst, bigendian)
134 p.assembly = '\n'.join(dis)+'\n'
135 self.add_case(p, initial_regs=initial_regs, initial_cr=cr)
136
137 def case_mfocrf_1(self):
138 lst = [f"mfocrf 2, 1"]
139 cr = 0x1234
140 self.add_case(Program(lst, bigendian), initial_cr=cr)
141
142 def case_mfocrf(self):
143 for i in range(1):
144 mask = 1 << random.randint(0, 7)
145 lst = [f"mfocrf 2, {mask}"]
146 cr = random.randint(0, (1 << 32)-1)
147 self.add_case(Program(lst, bigendian), initial_cr=cr)
148
149 def case_isel_0(self):
150 lst = [ "isel 4, 1, 2, 31"
151 ]
152 initial_regs = [0] * 32
153 initial_regs[1] = 0x1004
154 initial_regs[2] = 0x1008
155 cr= 0x1ee
156 self.add_case(Program(lst, bigendian),
157 initial_regs=initial_regs, initial_cr=cr)
158
159 def case_isel_1(self):
160 lst = [ "isel 4, 1, 2, 30"
161 ]
162 initial_regs = [0] * 32
163 initial_regs[1] = 0x1004
164 initial_regs[2] = 0x1008
165 cr= 0x1ee
166 self.add_case(Program(lst, bigendian),
167 initial_regs=initial_regs, initial_cr=cr)
168
169 def case_isel_2(self):
170 lst = [ "isel 4, 1, 2, 2"
171 ]
172 initial_regs = [0] * 32
173 initial_regs[1] = 0x1004
174 initial_regs[2] = 0x1008
175 cr= 0x1ee
176 self.add_case(Program(lst, bigendian),
177 initial_regs=initial_regs, initial_cr=cr)
178
179 def case_isel_3(self):
180 lst = [ "isel 1, 2, 3, 13"
181 ]
182 initial_regs = [0] * 32
183 initial_regs[2] = 0x1004
184 initial_regs[3] = 0x1008
185 cr= 0x5d677571b8229f1
186 cr= 0x1b8229f1
187 self.add_case(Program(lst, bigendian),
188 initial_regs=initial_regs, initial_cr=cr)
189
190 def case_isel(self):
191 for i in range(20):
192 bc = random.randint(0, 31)
193 lst = [f"isel 1, 2, 3, {bc}"]
194 cr = random.randint(0, (1 << 64)-1)
195 initial_regs = [0] * 32
196 #initial_regs[2] = random.randint(0, (1 << 64)-1)
197 #initial_regs[3] = random.randint(0, (1 << 64)-1)
198 initial_regs[2] = i*2+1
199 initial_regs[3] = i*2+2
200 self.add_case(Program(lst, bigendian),
201 initial_regs=initial_regs, initial_cr=cr)
202
203 def case_setb(self):
204 for i in range(20):
205 bfa = random.randint(0, 7)
206 lst = [f"setb 1, {bfa}"]
207 cr = random.randint(0, (1 << 32)-1)
208 self.add_case(Program(lst, bigendian), initial_cr=cr)
209
210 def case_regression_setb(self):
211 lst = [f"setb 1, 6"]
212 cr = random.randint(0, 0x66f6b106)
213 self.add_case(Program(lst, bigendian), initial_cr=cr)
214
215 def case_ilang(self):
216 pspec = CRPipeSpec(id_wid=2)
217 alu = CRBasePipe(pspec)
218 vl = rtlil.convert(alu, ports=alu.ports())
219 with open("cr_pipeline.il", "w") as f:
220 f.write(vl)
221
222
223 def get_cu_inputs(dec2, sim):
224 """naming (res) must conform to CRFunctionUnit input regspec
225 """
226 res = {}
227 full_reg = yield dec2.e.do.read_cr_whole.data
228 full_reg_ok = yield dec2.e.do.read_cr_whole.ok
229 full_cr_mask = mask_extend(full_reg, 8, 4)
230
231 # full CR
232 print(sim.cr.value)
233 if full_reg_ok:
234 res['full_cr'] = sim.cr.value & full_cr_mask
235 else:
236 yield from ALUHelpers.get_sim_cr_a(res, sim, dec2) # CR A
237 yield from ALUHelpers.get_sim_cr_b(res, sim, dec2) # CR B
238 yield from ALUHelpers.get_sim_cr_c(res, sim, dec2) # CR C
239
240 yield from ALUHelpers.get_sim_int_ra(res, sim, dec2) # RA
241 yield from ALUHelpers.get_sim_int_rb(res, sim, dec2) # RB
242
243 print("get inputs", res)
244 return res
245
246
247 class TestRunner(unittest.TestCase):
248 def __init__(self, test_data):
249 super().__init__("run_all")
250 self.test_data = test_data
251
252 def set_inputs(self, alu, dec2, simulator):
253 inp = yield from get_cu_inputs(dec2, simulator)
254 yield from ALUHelpers.set_full_cr(alu, dec2, inp)
255 yield from ALUHelpers.set_cr_a(alu, dec2, inp)
256 yield from ALUHelpers.set_cr_b(alu, dec2, inp)
257 yield from ALUHelpers.set_cr_c(alu, dec2, inp)
258 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
259 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
260
261 def assert_outputs(self, alu, dec2, simulator, code):
262 whole_reg_ok = yield dec2.e.do.write_cr_whole.ok
263 whole_reg_data = yield dec2.e.do.write_cr_whole.data
264 full_cr_mask = mask_extend(whole_reg_data, 8, 4)
265
266 cr_en = yield dec2.e.write_cr.ok
267 if whole_reg_ok:
268 full_cr = yield alu.n.data_o.full_cr.data & full_cr_mask
269 expected_cr = simulator.cr.value
270 print("CR whole: expected %x, actual: %x mask: %x" % \
271 (expected_cr, full_cr, full_cr_mask))
272 # HACK: only look at the bits that we expected to change
273 self.assertEqual(expected_cr & full_cr_mask, full_cr, code)
274 elif cr_en:
275 cr_sel = yield dec2.e.write_cr.data
276 expected_cr = simulator.cr.value
277 print(f"CR whole: {expected_cr:x}, sel {cr_sel}")
278 expected_cr = simulator.crl[cr_sel].get_range().value
279 real_cr = yield alu.n.data_o.cr.data
280 print(f"CR part: expected {expected_cr:x}, actual: {real_cr:x}")
281 self.assertEqual(expected_cr, real_cr, code)
282 alu_out = yield alu.n.data_o.o.data
283 out_reg_valid = yield dec2.e.write_reg.ok
284 if out_reg_valid:
285 write_reg_idx = yield dec2.e.write_reg.data
286 expected = simulator.gpr(write_reg_idx).value
287 print(f"expected {expected:x}, actual: {alu_out:x}")
288 self.assertEqual(expected, alu_out, code)
289
290 def execute(self, alu, instruction, pdecode2, test):
291 program = test.program
292 sim = ISA(pdecode2, test.regs, test.sprs, test.cr, test.mem,
293 test.msr,
294 bigendian=bigendian)
295 gen = program.generate_instructions()
296 instructions = list(zip(gen, program.assembly.splitlines()))
297
298 index = sim.pc.CIA.value//4
299 while index < len(instructions):
300 ins, code = instructions[index]
301
302 print("0x{:X}".format(ins & 0xffffffff))
303 print(code)
304
305 # ask the decoder to decode this binary data (endian'd)
306 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
307 yield instruction.eq(ins) # raw binary instr.
308 yield Settle()
309 yield from self.set_inputs(alu, pdecode2, sim)
310 yield alu.p.valid_i.eq(1)
311 fn_unit = yield pdecode2.e.do.fn_unit
312 self.assertEqual(fn_unit, Function.CR.value, code)
313 yield
314 opname = code.split(' ')[0]
315 yield from sim.call(opname)
316 index = sim.pc.CIA.value//4
317
318 vld = yield alu.n.valid_o
319 while not vld:
320 yield
321 vld = yield alu.n.valid_o
322 yield
323 yield from self.assert_outputs(alu, pdecode2, sim, code)
324
325 def run_all(self):
326 m = Module()
327 comb = m.d.comb
328 instruction = Signal(32)
329
330 pdecode = create_pdecode()
331
332 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
333
334 pspec = CRPipeSpec(id_wid=2)
335 m.submodules.alu = alu = CRBasePipe(pspec)
336
337 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
338 comb += alu.n.ready_i.eq(1)
339 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
340 sim = Simulator(m)
341
342 sim.add_clock(1e-6)
343
344 def process():
345 for test in self.test_data:
346 print(test.name)
347 with self.subTest(test.name):
348 yield from self.execute(alu, instruction, pdecode2, test)
349
350 sim.add_sync_process(process)
351 with sim.write_vcd("cr_simulator.vcd"):
352 sim.run()
353
354
355 if __name__ == "__main__":
356 unittest.main(exit=False)
357 suite = unittest.TestSuite()
358 suite.addTest(TestRunner(CRTestCase().test_data))
359
360 runner = unittest.TextTestRunner()
361 runner.run(suite)