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