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