use sub-test in logical test_pipe_caller
[soc.git] / src / soc / fu / logical / test / test_pipe_caller.py
1 from nmigen import Module, Signal
2 from nmigen.back.pysim import Simulator, Delay, Settle
3 from nmutil.formaltest import FHDLTestCase
4 from nmigen.cli import rtlil
5 import unittest
6 from soc.decoder.isa.caller import ISACaller, special_sprs
7 from soc.decoder.power_decoder import (create_pdecode)
8 from soc.decoder.power_decoder2 import (PowerDecode2)
9 from soc.decoder.power_enums import (XER_bits, Function)
10 from soc.decoder.selectable_int import SelectableInt
11 from soc.simulator.program import Program
12 from soc.decoder.isa.all import ISA
13 from soc.config.endian import bigendian
14
15
16 from soc.fu.test.common import TestAccumulatorBase, TestCase, ALUHelpers
17 from soc.fu.logical.pipeline import LogicalBasePipe
18 from soc.fu.logical.pipe_data import LogicalPipeSpec
19 import random
20
21
22 def get_cu_inputs(dec2, sim):
23 """naming (res) must conform to LogicalFunctionUnit input regspec
24 """
25 res = {}
26
27 yield from ALUHelpers.get_sim_int_ra(res, sim, dec2) # RA
28 yield from ALUHelpers.get_sim_int_rb(res, sim, dec2) # RB
29
30 return res
31
32
33 def set_alu_inputs(alu, dec2, sim):
34 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
35 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
36 # and place it into data_i.b
37
38 inp = yield from get_cu_inputs(dec2, sim)
39 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
40 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
41
42
43 # This test bench is a bit different than is usual. Initially when I
44 # was writing it, I had all of the tests call a function to create a
45 # device under test and simulator, initialize the dut, run the
46 # simulation for ~2 cycles, and assert that the dut output what it
47 # should have. However, this was really slow, since it needed to
48 # create and tear down the dut and simulator for every test case.
49
50 # Now, instead of doing that, every test case in ALUTestCase puts some
51 # data into the test_data list below, describing the instructions to
52 # be tested and the initial state. Once all the tests have been run,
53 # test_data gets passed to TestRunner which then sets up the DUT and
54 # simulator once, runs all the data through it, and asserts that the
55 # results match the pseudocode sim at every cycle.
56
57 # By doing this, I've reduced the time it takes to run the test suite
58 # massively. Before, it took around 1 minute on my computer, now it
59 # takes around 3 seconds
60
61
62 class LogicalTestCase(TestAccumulatorBase):
63
64 def case_complement(self):
65 insns = ["andc", "orc", "nand", "nor"]
66 for i in range(40):
67 choice = random.choice(insns)
68 lst = [f"{choice} 3, 1, 2"]
69 initial_regs = [0] * 32
70 initial_regs[1] = random.randint(0, (1 << 64)-1)
71 initial_regs[2] = random.randint(0, (1 << 64)-1)
72 self.add_case(Program(lst, bigendian), initial_regs)
73
74 def case_rand(self):
75 insns = ["and", "or", "xor", "eqv"]
76 for i in range(40):
77 choice = random.choice(insns)
78 lst = [f"{choice} 3, 1, 2"]
79 initial_regs = [0] * 32
80 initial_regs[1] = random.randint(0, (1 << 64)-1)
81 initial_regs[2] = random.randint(0, (1 << 64)-1)
82 self.add_case(Program(lst, bigendian), initial_regs)
83
84 def case_rand_imm_logical(self):
85 insns = ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
86 for i in range(10):
87 choice = random.choice(insns)
88 imm = random.randint(0, (1 << 16)-1)
89 lst = [f"{choice} 3, 1, {imm}"]
90 print(lst)
91 initial_regs = [0] * 32
92 initial_regs[1] = random.randint(0, (1 << 64)-1)
93 self.add_case(Program(lst, bigendian), initial_regs)
94
95 def case_cntz(self):
96 insns = ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
97 for i in range(100):
98 choice = random.choice(insns)
99 lst = [f"{choice} 3, 1"]
100 print(lst)
101 initial_regs = [0] * 32
102 initial_regs[1] = random.randint(0, (1 << 64)-1)
103 self.add_case(Program(lst, bigendian), initial_regs)
104
105 def case_parity(self):
106 insns = ["prtyw", "prtyd"]
107 for i in range(10):
108 choice = random.choice(insns)
109 lst = [f"{choice} 3, 1"]
110 print(lst)
111 initial_regs = [0] * 32
112 initial_regs[1] = random.randint(0, (1 << 64)-1)
113 self.add_case(Program(lst, bigendian), initial_regs)
114
115 def case_popcnt(self):
116 insns = ["popcntb", "popcntw", "popcntd"]
117 for i in range(10):
118 choice = random.choice(insns)
119 lst = [f"{choice} 3, 1"]
120 print(lst)
121 initial_regs = [0] * 32
122 initial_regs[1] = random.randint(0, (1 << 64)-1)
123 self.add_case(Program(lst, bigendian), initial_regs)
124
125 def case_popcnt_edge(self):
126 insns = ["popcntb", "popcntw", "popcntd"]
127 for choice in insns:
128 lst = [f"{choice} 3, 1"]
129 initial_regs = [0] * 32
130 initial_regs[1] = -1
131 self.add_case(Program(lst, bigendian), initial_regs)
132
133 def case_cmpb(self):
134 lst = ["cmpb 3, 1, 2"]
135 initial_regs = [0] * 32
136 initial_regs[1] = 0xdeadbeefcafec0de
137 initial_regs[2] = 0xd0adb0000afec1de
138 self.add_case(Program(lst, bigendian), initial_regs)
139
140 def case_bpermd(self):
141 lst = ["bpermd 3, 1, 2"]
142 for i in range(20):
143 initial_regs = [0] * 32
144 initial_regs[1] = 1 << random.randint(0, 63)
145 initial_regs[2] = 0xdeadbeefcafec0de
146 self.add_case(Program(lst, bigendian), initial_regs)
147
148 def case_ilang(self):
149 pspec = LogicalPipeSpec(id_wid=2)
150 alu = LogicalBasePipe(pspec)
151 vl = rtlil.convert(alu, ports=alu.ports())
152 with open("logical_pipeline.il", "w") as f:
153 f.write(vl)
154
155
156 class TestRunner(FHDLTestCase):
157 def __init__(self, test_data):
158 super().__init__("run_all")
159 self.test_data = test_data
160
161 def execute(self, alu,instruction, pdecode2, test):
162 print(test.name)
163 program = test.program
164 self.subTest(test.name)
165 simulator = ISA(pdecode2, test.regs, test.sprs, test.cr,
166 test.mem, test.msr,
167 bigendian=bigendian)
168 gen = program.generate_instructions()
169 instructions = list(zip(gen, program.assembly.splitlines()))
170
171 index = simulator.pc.CIA.value//4
172 while index < len(instructions):
173 ins, code = instructions[index]
174
175 print("0x{:X}".format(ins & 0xffffffff))
176 print(code)
177
178 # ask the decoder to decode this binary data (endian'd)
179 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
180 yield instruction.eq(ins) # raw binary instr.
181 yield Settle()
182 fn_unit = yield pdecode2.e.do.fn_unit
183 self.assertEqual(fn_unit, Function.LOGICAL.value, code)
184 yield from set_alu_inputs(alu, pdecode2, simulator)
185
186 # set valid for one cycle, propagate through pipeline...
187 yield alu.p.valid_i.eq(1)
188 yield
189 yield alu.p.valid_i.eq(0)
190
191 opname = code.split(' ')[0]
192 yield from simulator.call(opname)
193 index = simulator.pc.CIA.value//4
194
195 vld = yield alu.n.valid_o
196 while not vld:
197 yield
198 vld = yield alu.n.valid_o
199 yield
200
201 yield from self.check_alu_outputs(alu, pdecode2,
202 simulator, code)
203 yield Settle()
204
205 def run_all(self):
206 m = Module()
207 comb = m.d.comb
208 instruction = Signal(32)
209
210 pdecode = create_pdecode()
211
212 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
213
214 pspec = LogicalPipeSpec(id_wid=2)
215 m.submodules.alu = alu = LogicalBasePipe(pspec)
216
217 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
218 comb += alu.n.ready_i.eq(1)
219 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
220 sim = Simulator(m)
221
222 sim.add_clock(1e-6)
223
224 def process():
225 for test in self.test_data:
226 print(test.name)
227 program = test.program
228 with self.subTest(test.name):
229 yield from self.execute(alu, instruction, pdecode2, test)
230
231 sim.add_sync_process(process)
232 with sim.write_vcd("logical_simulator.vcd", "logical_simulator.gtkw",
233 traces=[]):
234 sim.run()
235
236 def check_alu_outputs(self, alu, dec2, sim, code):
237
238 rc = yield dec2.e.do.rc.data
239 cridx_ok = yield dec2.e.write_cr.ok
240 cridx = yield dec2.e.write_cr.data
241
242 print("check extra output", repr(code), cridx_ok, cridx)
243 if rc:
244 self.assertEqual(cridx, 0, code)
245
246 sim_o = {}
247 res = {}
248
249 yield from ALUHelpers.get_cr_a(res, alu, dec2)
250 yield from ALUHelpers.get_int_o(res, alu, dec2)
251
252 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
253 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
254
255 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))
256 ALUHelpers.check_int_o(self, res, sim_o, code)
257
258
259 if __name__ == "__main__":
260 unittest.main(exit=False)
261 suite = unittest.TestSuite()
262 suite.addTest(TestRunner(LogicalTestCase().test_data))
263
264 runner = unittest.TextTestRunner()
265 runner.run(suite)