add additional CR regression tests
[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_mfocrf_1(self):
114 lst = [f"mfocrf 2, 1"]
115 cr = 0x1234
116 self.add_case(Program(lst, bigendian), initial_cr=cr)
117
118 def case_mfocrf(self):
119 for i in range(1):
120 mask = 1 << random.randint(0, 7)
121 lst = [f"mfocrf 2, {mask}"]
122 cr = random.randint(0, (1 << 32)-1)
123 self.add_case(Program(lst, bigendian), initial_cr=cr)
124
125 def case_isel(self):
126 for i in range(20):
127 bc = random.randint(0, 31)
128 lst = [f"isel 1, 2, 3, {bc}"]
129 cr = random.randint(0, (1 << 64)-1)
130 initial_regs = [0] * 32
131 initial_regs[2] = random.randint(0, (1 << 64)-1)
132 initial_regs[3] = random.randint(0, (1 << 64)-1)
133 #initial_regs[2] = i*2
134 #initial_regs[3] = i*2+1
135 self.add_case(Program(lst, bigendian),
136 initial_regs=initial_regs, initial_cr=cr)
137
138 def case_setb(self):
139 for i in range(20):
140 bfa = random.randint(0, 7)
141 lst = [f"setb 1, {bfa}"]
142 cr = random.randint(0, (1 << 32)-1)
143 self.add_case(Program(lst, bigendian), initial_cr=cr)
144
145 def case_regression_setb(self):
146 lst = [f"setb 1, 6"]
147 cr = random.randint(0, 0x66f6b106)
148 self.add_case(Program(lst, bigendian), initial_cr=cr)
149
150 def case_ilang(self):
151 pspec = CRPipeSpec(id_wid=2)
152 alu = CRBasePipe(pspec)
153 vl = rtlil.convert(alu, ports=alu.ports())
154 with open("cr_pipeline.il", "w") as f:
155 f.write(vl)
156
157
158 def get_cu_inputs(dec2, sim):
159 """naming (res) must conform to CRFunctionUnit input regspec
160 """
161 res = {}
162 full_reg = yield dec2.e.do.read_cr_whole.data
163 full_reg_ok = yield dec2.e.do.read_cr_whole.ok
164 full_cr_mask = mask_extend(full_reg, 8, 4)
165
166 # full CR
167 print(sim.cr.get_range().value)
168 if full_reg_ok:
169 res['full_cr'] = sim.cr.get_range().value & full_cr_mask
170 else:
171 # CR A
172 cr1_en = yield dec2.e.read_cr1.ok
173 if cr1_en:
174 cr1_sel = yield dec2.e.read_cr1.data
175 res['cr_a'] = sim.crl[cr1_sel].get_range().value
176 cr2_en = yield dec2.e.read_cr2.ok
177 # CR B
178 if cr2_en:
179 cr2_sel = yield dec2.e.read_cr2.data
180 res['cr_b'] = sim.crl[cr2_sel].get_range().value
181 cr3_en = yield dec2.e.read_cr3.ok
182 # CR C
183 if cr3_en:
184 cr3_sel = yield dec2.e.read_cr3.data
185 res['cr_c'] = sim.crl[cr3_sel].get_range().value
186
187 # RA/RC
188 reg1_ok = yield dec2.e.read_reg1.ok
189 if reg1_ok:
190 data1 = yield dec2.e.read_reg1.data
191 res['ra'] = sim.gpr(data1).value
192
193 # RB (or immediate)
194 reg2_ok = yield dec2.e.read_reg2.ok
195 if reg2_ok:
196 data2 = yield dec2.e.read_reg2.data
197 res['rb'] = sim.gpr(data2).value
198
199 print("get inputs", res)
200 return res
201
202
203 class TestRunner(unittest.TestCase):
204 def __init__(self, test_data):
205 super().__init__("run_all")
206 self.test_data = test_data
207
208 def set_inputs(self, alu, dec2, simulator):
209 inp = yield from get_cu_inputs(dec2, simulator)
210 yield from ALUHelpers.set_full_cr(alu, dec2, inp)
211 yield from ALUHelpers.set_cr_a(alu, dec2, inp)
212 yield from ALUHelpers.set_cr_b(alu, dec2, inp)
213 yield from ALUHelpers.set_cr_c(alu, dec2, inp)
214 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
215 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
216
217 def assert_outputs(self, alu, dec2, simulator, code):
218 whole_reg_ok = yield dec2.e.do.write_cr_whole.ok
219 whole_reg_data = yield dec2.e.do.write_cr_whole.data
220 full_cr_mask = mask_extend(whole_reg_data, 8, 4)
221
222 cr_en = yield dec2.e.write_cr.ok
223 if whole_reg_ok:
224 full_cr = yield alu.n.data_o.full_cr.data & full_cr_mask
225 expected_cr = simulator.cr.get_range().value
226 print("CR whole: expected %x, actual: %x mask: %x" % \
227 (expected_cr, full_cr, full_cr_mask))
228 # HACK: only look at the bits that we expected to change
229 self.assertEqual(expected_cr & full_cr_mask, full_cr, code)
230 elif cr_en:
231 cr_sel = yield dec2.e.write_cr.data
232 expected_cr = simulator.cr.get_range().value
233 print(f"CR whole: {expected_cr:x}, sel {cr_sel}")
234 expected_cr = simulator.crl[cr_sel].get_range().value
235 real_cr = yield alu.n.data_o.cr.data
236 print(f"CR part: expected {expected_cr:x}, actual: {real_cr:x}")
237 self.assertEqual(expected_cr, real_cr, code)
238 alu_out = yield alu.n.data_o.o.data
239 out_reg_valid = yield dec2.e.write_reg.ok
240 if out_reg_valid:
241 write_reg_idx = yield dec2.e.write_reg.data
242 expected = simulator.gpr(write_reg_idx).value
243 print(f"expected {expected:x}, actual: {alu_out:x}")
244 self.assertEqual(expected, alu_out, code)
245
246 def execute(self, alu, instruction, pdecode2, test):
247 program = test.program
248 sim = ISA(pdecode2, test.regs, test.sprs, test.cr, test.mem,
249 test.msr,
250 bigendian=bigendian)
251 gen = program.generate_instructions()
252 instructions = list(zip(gen, program.assembly.splitlines()))
253
254 index = sim.pc.CIA.value//4
255 while index < len(instructions):
256 ins, code = instructions[index]
257
258 print("0x{:X}".format(ins & 0xffffffff))
259 print(code)
260
261 # ask the decoder to decode this binary data (endian'd)
262 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
263 yield instruction.eq(ins) # raw binary instr.
264 yield Settle()
265 yield from self.set_inputs(alu, pdecode2, sim)
266 yield alu.p.valid_i.eq(1)
267 fn_unit = yield pdecode2.e.do.fn_unit
268 self.assertEqual(fn_unit, Function.CR.value, code)
269 yield
270 opname = code.split(' ')[0]
271 yield from sim.call(opname)
272 index = sim.pc.CIA.value//4
273
274 vld = yield alu.n.valid_o
275 while not vld:
276 yield
277 vld = yield alu.n.valid_o
278 yield
279 yield from self.assert_outputs(alu, pdecode2, sim, code)
280
281 def run_all(self):
282 m = Module()
283 comb = m.d.comb
284 instruction = Signal(32)
285
286 pdecode = create_pdecode()
287
288 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
289
290 pspec = CRPipeSpec(id_wid=2)
291 m.submodules.alu = alu = CRBasePipe(pspec)
292
293 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
294 comb += alu.n.ready_i.eq(1)
295 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
296 sim = Simulator(m)
297
298 sim.add_clock(1e-6)
299
300 def process():
301 for test in self.test_data:
302 print(test.name)
303 with self.subTest(test.name):
304 yield from self.execute(alu, instruction, pdecode2, test)
305
306 sim.add_sync_process(process)
307 with sim.write_vcd("cr_simulator.vcd"):
308 sim.run()
309
310
311 if __name__ == "__main__":
312 unittest.main(exit=False)
313 suite = unittest.TestSuite()
314 suite.addTest(TestRunner(CRTestCase().test_data))
315
316 runner = unittest.TextTestRunner()
317 runner.run(suite)