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