b29d8a45a99eca8d0f40798f0818a0669e44f2c0
[soc.git] / src / soc / fu / branch / 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, MicrOp)
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.regfile.regfiles import FastRegs
14 from soc.config.endian import bigendian
15
16 from soc.fu.test.common import TestCase, ALUHelpers
17 from soc.fu.branch.pipeline import BranchBasePipe
18 from soc.fu.branch.pipe_data import BranchPipeSpec
19 import random
20
21 from soc.regfile.util import fast_reg_to_spr # HACK!
22
23
24 def get_rec_width(rec):
25 recwidth = 0
26 # Setup random inputs for dut.op
27 for p in rec.ports():
28 width = p.width
29 recwidth += width
30 return recwidth
31
32
33 # This test bench is a bit different than is usual. Initially when I
34 # was writing it, I had all of the tests call a function to create a
35 # device under test and simulator, initialize the dut, run the
36 # simulation for ~2 cycles, and assert that the dut output what it
37 # should have. However, this was really slow, since it needed to
38 # create and tear down the dut and simulator for every test case.
39
40 # Now, instead of doing that, every test case in ALUTestCase puts some
41 # data into the test_data list below, describing the instructions to
42 # be tested and the initial state. Once all the tests have been run,
43 # test_data gets passed to TestRunner which then sets up the DUT and
44 # simulator once, runs all the data through it, and asserts that the
45 # results match the pseudocode sim at every cycle.
46
47 # By doing this, I've reduced the time it takes to run the test suite
48 # massively. Before, it took around 1 minute on my computer, now it
49 # takes around 3 seconds
50
51
52 def get_cu_inputs(dec2, sim):
53 """naming (res) must conform to BranchFunctionUnit input regspec
54 """
55 res = {}
56
57 # CIA (PC)
58 #res['cia'] = sim.pc.CIA.value
59
60 yield from ALUHelpers.get_sim_fast_spr1(res, sim, dec2)
61 yield from ALUHelpers.get_sim_fast_spr2(res, sim, dec2)
62 yield from ALUHelpers.get_sim_cr_a(res, sim, dec2)
63
64 print ("get inputs", res)
65 return res
66
67
68 class BranchTestCase(FHDLTestCase):
69 test_data = []
70 def __init__(self, name):
71 super().__init__(name)
72 self.test_name = name
73
74 def run_tst_program(self, prog, initial_regs=None,
75 initial_sprs=None, initial_cr=0):
76 tc = TestCase(prog, self.test_name,
77 initial_regs, initial_sprs, initial_cr)
78 self.test_data.append(tc)
79
80 def test_0_regression_unconditional(self):
81 for i in range(2):
82 imm = random.randrange(-1<<23, (1<<23)-1) * 4
83 lst = [f"bl {imm}"]
84 initial_regs = [0] * 32
85 self.run_tst_program(Program(lst, bigendian), initial_regs)
86
87 def test_unconditional(self):
88 choices = ["b", "ba", "bl", "bla"]
89 for i in range(20):
90 choice = random.choice(choices)
91 imm = random.randrange(-1<<23, (1<<23)-1) * 4
92 lst = [f"{choice} {imm}"]
93 initial_regs = [0] * 32
94 self.run_tst_program(Program(lst, bigendian), initial_regs)
95
96 def test_bc_cr(self):
97 for i in range(20):
98 bc = random.randrange(-1<<13, (1<<13)-1) * 4
99 bo = random.choice([0b01100, 0b00100, 0b10100])
100 bi = random.randrange(0, 31)
101 cr = random.randrange(0, (1<<32)-1)
102 lst = [f"bc {bo}, {bi}, {bc}"]
103 initial_regs = [0] * 32
104 self.run_tst_program(Program(lst, bigendian), initial_cr=cr)
105
106 def test_bc_ctr(self):
107 for i in range(20):
108 bc = random.randrange(-1<<13, (1<<13)-1) * 4
109 bo = random.choice([0, 2, 8, 10, 16, 18])
110 bi = random.randrange(0, 31)
111 cr = random.randrange(0, (1<<32)-1)
112 ctr = random.randint(0, (1<<32)-1)
113 lst = [f"bc {bo}, {bi}, {bc}"]
114 initial_sprs={9: SelectableInt(ctr, 64)}
115 self.run_tst_program(Program(lst, bigendian),
116 initial_sprs=initial_sprs,
117 initial_cr=cr)
118
119 def test_bc_reg(self):
120 # XXX: bcctr and bcctrl time out (irony: they're counters)
121 choices = ["bclr", "bclrl", "bcctr", "bcctrl", "bctar", "bctarl"]
122 for insn in choices:
123 for i in range(20):
124 bh = random.randrange(0, 3)
125 bo = random.choice([4, 12])
126 bi = random.randrange(0, 31)
127 cr = random.randrange(0, (1<<32)-1)
128 ctr = random.randint(0, (1<<32)-1)
129 lr = random.randint(0, (1<<64)-1) & ~3
130 tar = random.randint(0, (1<<64)-1) & ~3
131 lst = [f"{insn} {bo}, {bi}, {bh}"]
132 initial_sprs={9: SelectableInt(ctr, 64),
133 8: SelectableInt(lr, 64),
134 815: SelectableInt(tar, 64)}
135 self.run_tst_program(Program(lst, bigendian),
136 initial_sprs=initial_sprs,
137 initial_cr=cr)
138
139 def test_ilang(self):
140 pspec = BranchPipeSpec(id_wid=2)
141 alu = BranchBasePipe(pspec)
142 vl = rtlil.convert(alu, ports=alu.ports())
143 with open("branch_pipeline.il", "w") as f:
144 f.write(vl)
145
146
147 class TestRunner(FHDLTestCase):
148 def __init__(self, test_data):
149 super().__init__("run_all")
150 self.test_data = test_data
151
152 def run_all(self):
153 m = Module()
154 comb = m.d.comb
155 instruction = Signal(32)
156
157 pdecode = create_pdecode()
158
159 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
160
161 pspec = BranchPipeSpec(id_wid=2)
162 m.submodules.branch = branch = BranchBasePipe(pspec)
163
164 comb += branch.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
165 comb += branch.p.valid_i.eq(1)
166 comb += branch.n.ready_i.eq(1)
167 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
168 sim = Simulator(m)
169
170 sim.add_clock(1e-6)
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 initial_cia = 0x2000
180 simulator.set_pc(initial_cia)
181 gen = program.generate_instructions()
182 instructions = list(zip(gen, program.assembly.splitlines()))
183
184 pc = simulator.pc.CIA.value
185 msr = simulator.msr.value
186 index = (pc - initial_cia)//4
187 while index < len(instructions) and index >= 0:
188 print(index)
189 ins, code = instructions[index]
190
191 print("0x{:X}".format(ins & 0xffffffff))
192 print(code)
193
194 # ask the decoder to decode this binary data (endian'd)
195 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
196 yield pdecode2.msr.eq(msr) # set MSR in pdecode2
197 yield pdecode2.cia.eq(pc) # set PC in pdecode2
198 yield instruction.eq(ins) # raw binary instr.
199 # note, here, the op will need further decoding in order
200 # to set the correct SPRs on SPR1/2/3. op_bc* require
201 # spr1 to be set to CTR, op_bctar require spr2 to be
202 # set to TAR, op_bclr* require spr2 to be set to LR.
203 # if op_sc*, op_rf* and op_hrfid are to be added here
204 # then additional op-decoding is required, accordingly
205 yield Settle()
206 lk = yield pdecode2.e.do.lk
207 print ("lk:", lk)
208 yield from self.set_inputs(branch, pdecode2, simulator)
209 fn_unit = yield pdecode2.e.do.fn_unit
210 self.assertEqual(fn_unit, Function.BRANCH.value, code)
211 yield
212 yield
213 opname = code.split(' ')[0]
214 prev_nia = simulator.pc.NIA.value
215 yield from simulator.call(opname)
216 pc = simulator.pc.CIA.value
217 msr = simulator.msr.value
218 index = (pc - initial_cia)//4
219
220 yield from self.assert_outputs(branch, pdecode2,
221 simulator, prev_nia, code)
222
223 sim.add_sync_process(process)
224 with sim.write_vcd("branch_simulator.vcd"):
225 sim.run()
226
227 def assert_outputs(self, branch, dec2, sim, prev_nia, code):
228 branch_taken = yield branch.n.data_o.nia.ok
229 sim_branch_taken = prev_nia != sim.pc.CIA
230 self.assertEqual(branch_taken, sim_branch_taken, code)
231 if branch_taken:
232 branch_addr = yield branch.n.data_o.nia.data
233 print(f"real: {branch_addr:x}, sim: {sim.pc.CIA.value:x}")
234 self.assertEqual(branch_addr, sim.pc.CIA.value, code)
235
236 # TODO: check write_fast1 as well (should contain CTR)
237
238 # TODO: this should be checking write_fast2
239 lk = yield dec2.e.do.lk
240 branch_lk = yield branch.n.data_o.lr.ok
241 self.assertEqual(lk, branch_lk, code)
242 if lk:
243 branch_lr = yield branch.n.data_o.lr.data
244 self.assertEqual(sim.spr['LR'], branch_lr, code)
245
246 def set_inputs(self, branch, dec2, sim):
247 print(f"cr0: {sim.crl[0].get_range()}")
248
249 inp = yield from get_cu_inputs(dec2, sim)
250
251 yield from ALUHelpers.set_fast_spr1(branch, dec2, inp)
252 yield from ALUHelpers.set_fast_spr2(branch, dec2, inp)
253 yield from ALUHelpers.set_cr_a(branch, dec2, inp)
254
255
256 if __name__ == "__main__":
257 unittest.main(exit=False)
258 suite = unittest.TestSuite()
259 suite.addTest(TestRunner(BranchTestCase.test_data))
260
261 runner = unittest.TextTestRunner()
262 runner.run(suite)