format code
[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 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(FHDLTestCase):
63 test_data = []
64
65 def __init__(self, name):
66 super().__init__(name)
67 self.test_name = name
68
69 def run_tst_program(self, prog, initial_regs=None, initial_sprs=None):
70 tc = TestCase(prog, self.test_name, initial_regs, initial_sprs)
71 self.test_data.append(tc)
72
73 def test_rand(self):
74 insns = ["and", "or", "xor"]
75 for i in range(40):
76 choice = random.choice(insns)
77 lst = [f"{choice} 3, 1, 2"]
78 initial_regs = [0] * 32
79 initial_regs[1] = random.randint(0, (1 << 64)-1)
80 initial_regs[2] = random.randint(0, (1 << 64)-1)
81 self.run_tst_program(Program(lst, bigendian), initial_regs)
82
83 def test_rand_imm_logical(self):
84 insns = ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
85 for i in range(10):
86 choice = random.choice(insns)
87 imm = random.randint(0, (1 << 16)-1)
88 lst = [f"{choice} 3, 1, {imm}"]
89 print(lst)
90 initial_regs = [0] * 32
91 initial_regs[1] = random.randint(0, (1 << 64)-1)
92 self.run_tst_program(Program(lst, bigendian), initial_regs)
93
94 def test_cntz(self):
95 insns = ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
96 for i in range(100):
97 choice = random.choice(insns)
98 lst = [f"{choice} 3, 1"]
99 print(lst)
100 initial_regs = [0] * 32
101 initial_regs[1] = random.randint(0, (1 << 64)-1)
102 self.run_tst_program(Program(lst, bigendian), initial_regs)
103
104 def test_parity(self):
105 insns = ["prtyw", "prtyd"]
106 for i in range(10):
107 choice = random.choice(insns)
108 lst = [f"{choice} 3, 1"]
109 print(lst)
110 initial_regs = [0] * 32
111 initial_regs[1] = random.randint(0, (1 << 64)-1)
112 self.run_tst_program(Program(lst, bigendian), initial_regs)
113
114 def test_popcnt(self):
115 insns = ["popcntb", "popcntw", "popcntd"]
116 for i in range(10):
117 choice = random.choice(insns)
118 lst = [f"{choice} 3, 1"]
119 print(lst)
120 initial_regs = [0] * 32
121 initial_regs[1] = random.randint(0, (1 << 64)-1)
122 self.run_tst_program(Program(lst, bigendian), initial_regs)
123
124 def test_popcnt_edge(self):
125 insns = ["popcntb", "popcntw", "popcntd"]
126 for choice in insns:
127 lst = [f"{choice} 3, 1"]
128 initial_regs = [0] * 32
129 initial_regs[1] = -1
130 self.run_tst_program(Program(lst, bigendian), initial_regs)
131
132 def test_cmpb(self):
133 lst = ["cmpb 3, 1, 2"]
134 initial_regs = [0] * 32
135 initial_regs[1] = 0xdeadbeefcafec0de
136 initial_regs[2] = 0xd0adb0000afec1de
137 self.run_tst_program(Program(lst, bigendian), initial_regs)
138
139 def test_bpermd(self):
140 lst = ["bpermd 3, 1, 2"]
141 for i in range(20):
142 initial_regs = [0] * 32
143 initial_regs[1] = 1 << random.randint(0, 63)
144 initial_regs[2] = 0xdeadbeefcafec0de
145 self.run_tst_program(Program(lst, bigendian), initial_regs)
146
147 def test_ilang(self):
148 pspec = LogicalPipeSpec(id_wid=2)
149 alu = LogicalBasePipe(pspec)
150 vl = rtlil.convert(alu, ports=alu.ports())
151 with open("logical_pipeline.il", "w") as f:
152 f.write(vl)
153
154
155 class TestRunner(FHDLTestCase):
156 def __init__(self, test_data):
157 super().__init__("run_all")
158 self.test_data = test_data
159
160 def run_all(self):
161 m = Module()
162 comb = m.d.comb
163 instruction = Signal(32)
164
165 pdecode = create_pdecode()
166
167 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
168
169 pspec = LogicalPipeSpec(id_wid=2)
170 m.submodules.alu = alu = LogicalBasePipe(pspec)
171
172 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
173 comb += alu.p.valid_i.eq(1)
174 comb += alu.n.ready_i.eq(1)
175 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
176 sim = Simulator(m)
177
178 sim.add_clock(1e-6)
179
180 def process():
181 for test in self.test_data:
182 print(test.name)
183 program = test.program
184 self.subTest(test.name)
185 simulator = ISA(pdecode2, test.regs, test.sprs, test.cr,
186 test.mem, test.msr,
187 bigendian=bigendian)
188 gen = program.generate_instructions()
189 instructions = list(zip(gen, program.assembly.splitlines()))
190
191 index = simulator.pc.CIA.value//4
192 while index < len(instructions):
193 ins, code = instructions[index]
194
195 print("0x{:X}".format(ins & 0xffffffff))
196 print(code)
197
198 # ask the decoder to decode this binary data (endian'd)
199 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
200 yield instruction.eq(ins) # raw binary instr.
201 yield Settle()
202 fn_unit = yield pdecode2.e.do.fn_unit
203 self.assertEqual(fn_unit, Function.LOGICAL.value, code)
204 yield from set_alu_inputs(alu, pdecode2, simulator)
205 yield
206 opname = code.split(' ')[0]
207 yield from simulator.call(opname)
208 index = simulator.pc.CIA.value//4
209
210 vld = yield alu.n.valid_o
211 while not vld:
212 yield
213 vld = yield alu.n.valid_o
214 yield
215
216 yield from self.check_alu_outputs(alu, pdecode2,
217 simulator, code)
218
219 sim.add_sync_process(process)
220 with sim.write_vcd("logical_simulator.vcd", "logical_simulator.gtkw",
221 traces=[]):
222 sim.run()
223
224 def check_alu_outputs(self, alu, dec2, sim, code):
225
226 rc = yield dec2.e.do.rc.data
227 cridx_ok = yield dec2.e.write_cr.ok
228 cridx = yield dec2.e.write_cr.data
229
230 print("check extra output", repr(code), cridx_ok, cridx)
231 if rc:
232 self.assertEqual(cridx, 0, code)
233
234 sim_o = {}
235 res = {}
236
237 yield from ALUHelpers.get_cr_a(res, alu, dec2)
238 yield from ALUHelpers.get_int_o(res, alu, dec2)
239
240 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
241 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
242
243 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))
244 ALUHelpers.check_int_o(self, res, sim_o, code)
245
246
247 if __name__ == "__main__":
248 unittest.main(exit=False)
249 suite = unittest.TestSuite()
250 suite.addTest(TestRunner(LogicalTestCase.test_data))
251
252 runner = unittest.TextTestRunner()
253 runner.run(suite)