fix bug in alu_fsm.py found by cxxsim: missing one cycle hold of ready_i
[soc.git] / src / soc / fu / alu / test / test_pipe_caller.py
1 from nmigen import Module, Signal
2 from nmigen.back.pysim import Delay, Settle
3 cxxsim = True
4 if cxxsim:
5 from nmigen.sim.cxxsim import Simulator
6 else:
7 from nmigen.back.pysim import Simulator
8
9 from nmutil.formaltest import FHDLTestCase
10 from nmigen.cli import rtlil
11 import unittest
12 from soc.decoder.isa.caller import ISACaller, special_sprs
13 from soc.decoder.power_decoder import (create_pdecode)
14 from soc.decoder.power_decoder2 import (PowerDecode2)
15 from soc.decoder.power_enums import (XER_bits, Function, MicrOp, CryIn)
16 from soc.decoder.selectable_int import SelectableInt
17 from soc.simulator.program import Program
18 from soc.decoder.isa.all import ISA
19 from soc.config.endian import bigendian
20
21 from soc.fu.test.common import (TestCase, ALUHelpers)
22 from soc.fu.alu.pipeline import ALUBasePipe
23 from soc.fu.alu.pipe_data import ALUPipeSpec
24 import random
25
26
27 def get_cu_inputs(dec2, sim):
28 """naming (res) must conform to ALUFunctionUnit input regspec
29 """
30 res = {}
31
32 yield from ALUHelpers.get_sim_int_ra(res, sim, dec2) # RA
33 yield from ALUHelpers.get_sim_int_rb(res, sim, dec2) # RB
34 yield from ALUHelpers.get_rd_sim_xer_ca(res, sim, dec2) # XER.ca
35 yield from ALUHelpers.get_sim_xer_so(res, sim, dec2) # XER.so
36
37 print ("alu get_cu_inputs", res)
38
39 return res
40
41
42
43 def set_alu_inputs(alu, dec2, sim):
44 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
45 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
46 # and place it into data_i.b
47
48 inp = yield from get_cu_inputs(dec2, sim)
49 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
50 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
51
52 yield from ALUHelpers.set_xer_ca(alu, dec2, inp)
53 yield from ALUHelpers.set_xer_so(alu, dec2, inp)
54
55
56 # This test bench is a bit different than is usual. Initially when I
57 # was writing it, I had all of the tests call a function to create a
58 # device under test and simulator, initialize the dut, run the
59 # simulation for ~2 cycles, and assert that the dut output what it
60 # should have. However, this was really slow, since it needed to
61 # create and tear down the dut and simulator for every test case.
62
63 # Now, instead of doing that, every test case in ALUTestCase puts some
64 # data into the test_data list below, describing the instructions to
65 # be tested and the initial state. Once all the tests have been run,
66 # test_data gets passed to TestRunner which then sets up the DUT and
67 # simulator once, runs all the data through it, and asserts that the
68 # results match the pseudocode sim at every cycle.
69
70 # By doing this, I've reduced the time it takes to run the test suite
71 # massively. Before, it took around 1 minute on my computer, now it
72 # takes around 3 seconds
73
74
75 class ALUTestCase(FHDLTestCase):
76 test_data = []
77
78 def __init__(self, name):
79 super().__init__(name)
80 self.test_name = name
81
82 def run_tst_program(self, prog, initial_regs=None, initial_sprs=None):
83 tc = TestCase(prog, self.test_name, initial_regs, initial_sprs)
84 self.test_data.append(tc)
85
86 def test_1_regression(self):
87 lst = [f"extsw 3, 1"]
88 initial_regs = [0] * 32
89 initial_regs[1] = 0xb6a1fc6c8576af91
90 self.run_tst_program(Program(lst, bigendian), initial_regs)
91 lst = [f"subf 3, 1, 2"]
92 initial_regs = [0] * 32
93 initial_regs[1] = 0x3d7f3f7ca24bac7b
94 initial_regs[2] = 0xf6b2ac5e13ee15c2
95 self.run_tst_program(Program(lst, bigendian), initial_regs)
96 lst = [f"subf 3, 1, 2"]
97 initial_regs = [0] * 32
98 initial_regs[1] = 0x833652d96c7c0058
99 initial_regs[2] = 0x1c27ecff8a086c1a
100 self.run_tst_program(Program(lst, bigendian), initial_regs)
101 lst = [f"extsb 3, 1"]
102 initial_regs = [0] * 32
103 initial_regs[1] = 0x7f9497aaff900ea0
104 self.run_tst_program(Program(lst, bigendian), initial_regs)
105 lst = [f"add. 3, 1, 2"]
106 initial_regs = [0] * 32
107 initial_regs[1] = 0xc523e996a8ff6215
108 initial_regs[2] = 0xe1e5b9cc9864c4a8
109 self.run_tst_program(Program(lst, bigendian), initial_regs)
110 lst = [f"add 3, 1, 2"]
111 initial_regs = [0] * 32
112 initial_regs[1] = 0x2e08ae202742baf8
113 initial_regs[2] = 0x86c43ece9efe5baa
114 self.run_tst_program(Program(lst, bigendian), initial_regs)
115
116 def test_rand(self):
117 insns = ["add", "add.", "subf"]
118 for i in range(40):
119 choice = random.choice(insns)
120 lst = [f"{choice} 3, 1, 2"]
121 initial_regs = [0] * 32
122 initial_regs[1] = random.randint(0, (1<<64)-1)
123 initial_regs[2] = random.randint(0, (1<<64)-1)
124 self.run_tst_program(Program(lst, bigendian), initial_regs)
125
126 def test_rand_imm(self):
127 insns = ["addi", "addis", "subfic"]
128 for i in range(10):
129 choice = random.choice(insns)
130 imm = random.randint(-(1<<15), (1<<15)-1)
131 lst = [f"{choice} 3, 1, {imm}"]
132 print(lst)
133 initial_regs = [0] * 32
134 initial_regs[1] = random.randint(0, (1<<64)-1)
135 self.run_tst_program(Program(lst, bigendian), initial_regs)
136
137 def test_0_adde(self):
138 lst = ["adde. 5, 6, 7"]
139 for i in range(10):
140 initial_regs = [0] * 32
141 initial_regs[6] = random.randint(0, (1<<64)-1)
142 initial_regs[7] = random.randint(0, (1<<64)-1)
143 initial_sprs = {}
144 xer = SelectableInt(0, 64)
145 xer[XER_bits['CA']] = 1
146 initial_sprs[special_sprs['XER']] = xer
147 self.run_tst_program(Program(lst, bigendian), initial_regs, initial_sprs)
148
149 def test_cmp(self):
150 lst = ["subf. 1, 6, 7",
151 "cmp cr2, 1, 6, 7"]
152 initial_regs = [0] * 32
153 initial_regs[6] = 0x10
154 initial_regs[7] = 0x05
155 self.run_tst_program(Program(lst, bigendian), initial_regs, {})
156
157 def test_extsb(self):
158 insns = ["extsb", "extsh", "extsw"]
159 for i in range(10):
160 choice = random.choice(insns)
161 lst = [f"{choice} 3, 1"]
162 print(lst)
163 initial_regs = [0] * 32
164 initial_regs[1] = random.randint(0, (1<<64)-1)
165 self.run_tst_program(Program(lst, bigendian), initial_regs)
166
167 def test_cmpeqb(self):
168 lst = ["cmpeqb cr1, 1, 2"]
169 for i in range(20):
170 initial_regs = [0] * 32
171 initial_regs[1] = i
172 initial_regs[2] = 0x0001030507090b0f
173 self.run_tst_program(Program(lst, bigendian), initial_regs, {})
174
175 def test_ilang(self):
176 pspec = ALUPipeSpec(id_wid=2)
177 alu = ALUBasePipe(pspec)
178 vl = rtlil.convert(alu, ports=alu.ports())
179 with open("alu_pipeline.il", "w") as f:
180 f.write(vl)
181
182
183 class TestRunner(FHDLTestCase):
184 def __init__(self, test_data):
185 super().__init__("run_all")
186 self.test_data = test_data
187
188 def run_all(self):
189 m = Module()
190 comb = m.d.comb
191 instruction = Signal(32)
192
193 pdecode = create_pdecode()
194
195 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
196
197 pspec = ALUPipeSpec(id_wid=2)
198 m.submodules.alu = alu = ALUBasePipe(pspec)
199
200 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
201 comb += alu.p.valid_i.eq(1)
202 comb += alu.n.ready_i.eq(1)
203 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
204 sim = Simulator(m)
205
206 sim.add_clock(1e-6)
207 def process():
208 for test in self.test_data:
209 print(test.name)
210 program = test.program
211 self.subTest(test.name)
212 sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
213 test.mem, test.msr,
214 bigendian=bigendian)
215 gen = program.generate_instructions()
216 instructions = list(zip(gen, program.assembly.splitlines()))
217
218 index = sim.pc.CIA.value//4
219 while index < len(instructions):
220 ins, code = instructions[index]
221
222 print("instruction: 0x{:X}".format(ins & 0xffffffff))
223 print(code)
224 if 'XER' in sim.spr:
225 so = 1 if sim.spr['XER'][XER_bits['SO']] else 0
226 ov = 1 if sim.spr['XER'][XER_bits['OV']] else 0
227 ov32 = 1 if sim.spr['XER'][XER_bits['OV32']] else 0
228 print ("before: so/ov/32", so, ov, ov32)
229
230 # ask the decoder to decode this binary data (endian'd)
231 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
232 yield instruction.eq(ins) # raw binary instr.
233 yield Settle()
234 fn_unit = yield pdecode2.e.do.fn_unit
235 self.assertEqual(fn_unit, Function.ALU.value)
236 yield from set_alu_inputs(alu, pdecode2, sim)
237 yield
238 opname = code.split(' ')[0]
239 yield from sim.call(opname)
240 index = sim.pc.CIA.value//4
241
242 vld = yield alu.n.valid_o
243 while not vld:
244 yield
245 vld = yield alu.n.valid_o
246 yield
247
248 yield from self.check_alu_outputs(alu, pdecode2, sim, code)
249
250 sim.add_sync_process(process)
251 if cxxsim:
252 sim.run()
253 else:
254 with sim.write_vcd("alu_simulator.vcd", "simulator.gtkw",
255 traces=[]):
256 sim.run()
257
258 def check_alu_outputs(self, alu, dec2, sim, code):
259
260 rc = yield dec2.e.do.rc.data
261 cridx_ok = yield dec2.e.write_cr.ok
262 cridx = yield dec2.e.write_cr.data
263
264 print ("check extra output", repr(code), cridx_ok, cridx)
265 if rc:
266 self.assertEqual(cridx, 0, code)
267
268 oe = yield dec2.e.do.oe.oe
269 oe_ok = yield dec2.e.do.oe.ok
270 if not oe or not oe_ok:
271 # if OE not enabled, XER SO and OV must correspondingly be false
272 so_ok = yield alu.n.data_o.xer_so.ok
273 ov_ok = yield alu.n.data_o.xer_ov.ok
274 self.assertEqual(so_ok, False, code)
275 self.assertEqual(ov_ok, False, code)
276
277 sim_o = {}
278 res = {}
279
280 yield from ALUHelpers.get_cr_a(res, alu, dec2)
281 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
282 yield from ALUHelpers.get_xer_ca(res, alu, dec2)
283 yield from ALUHelpers.get_int_o(res, alu, dec2)
284 yield from ALUHelpers.get_xer_so(res, alu, dec2)
285
286 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
287 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
288 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
289 yield from ALUHelpers.get_wr_sim_xer_ca(sim_o, sim, dec2)
290 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
291
292 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))
293 ALUHelpers.check_xer_ov(self, res, sim_o, code)
294 ALUHelpers.check_xer_ca(self, res, sim_o, code)
295 ALUHelpers.check_int_o(self, res, sim_o, code)
296 ALUHelpers.check_xer_so(self, res, sim_o, code)
297
298
299 if __name__ == "__main__":
300 unittest.main(exit=False)
301 suite = unittest.TestSuite()
302 suite.addTest(TestRunner(ALUTestCase.test_data))
303
304 runner = unittest.TextTestRunner()
305 runner.run(suite)