Merge remote-tracking branch 'origin/master'
[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
71 def __init__(self, name):
72 super().__init__(name)
73 self.test_name = name
74
75 def run_tst_program(self, prog, initial_regs=None,
76 initial_sprs=None, initial_cr=0):
77 tc = TestCase(prog, self.test_name,
78 initial_regs, initial_sprs, initial_cr)
79 self.test_data.append(tc)
80
81 def test_0_regression_unconditional(self):
82 for i in range(2):
83 imm = random.randrange(-1 << 23, (1 << 23)-1) * 4
84 lst = [f"bl {imm}"]
85 initial_regs = [0] * 32
86 self.run_tst_program(Program(lst, bigendian), initial_regs)
87
88 def test_unconditional(self):
89 choices = ["b", "ba", "bl", "bla"]
90 for i in range(20):
91 choice = random.choice(choices)
92 imm = random.randrange(-1 << 23, (1 << 23)-1) * 4
93 lst = [f"{choice} {imm}"]
94 initial_regs = [0] * 32
95 self.run_tst_program(Program(lst, bigendian), initial_regs)
96
97 def test_bc_cr(self):
98 for i in range(20):
99 bc = random.randrange(-1 << 13, (1 << 13)-1) * 4
100 bo = random.choice([0b01100, 0b00100, 0b10100])
101 bi = random.randrange(0, 31)
102 cr = random.randrange(0, (1 << 32)-1)
103 lst = [f"bc {bo}, {bi}, {bc}"]
104 initial_regs = [0] * 32
105 self.run_tst_program(Program(lst, bigendian), initial_cr=cr)
106
107 def test_bc_ctr(self):
108 for i in range(20):
109 bc = random.randrange(-1 << 13, (1 << 13)-1) * 4
110 bo = random.choice([0, 2, 8, 10, 16, 18])
111 bi = random.randrange(0, 31)
112 cr = random.randrange(0, (1 << 32)-1)
113 ctr = random.randint(0, (1 << 32)-1)
114 lst = [f"bc {bo}, {bi}, {bc}"]
115 initial_sprs = {9: SelectableInt(ctr, 64)}
116 self.run_tst_program(Program(lst, bigendian),
117 initial_sprs=initial_sprs,
118 initial_cr=cr)
119
120 def test_bc_reg(self):
121 # XXX: bcctr and bcctrl time out (irony: they're counters)
122 choices = ["bclr", "bclrl", "bcctr", "bcctrl", "bctar", "bctarl"]
123 for insn in choices:
124 for i in range(20):
125 bh = random.randrange(0, 3)
126 bo = random.choice([4, 12])
127 bi = random.randrange(0, 31)
128 cr = random.randrange(0, (1 << 32)-1)
129 ctr = random.randint(0, (1 << 32)-1)
130 lr = random.randint(0, (1 << 64)-1) & ~3
131 tar = random.randint(0, (1 << 64)-1) & ~3
132 lst = [f"{insn} {bo}, {bi}, {bh}"]
133 initial_sprs = {9: SelectableInt(ctr, 64),
134 8: SelectableInt(lr, 64),
135 815: SelectableInt(tar, 64)}
136 self.run_tst_program(Program(lst, bigendian),
137 initial_sprs=initial_sprs,
138 initial_cr=cr)
139
140 def test_ilang(self):
141 pspec = BranchPipeSpec(id_wid=2)
142 alu = BranchBasePipe(pspec)
143 vl = rtlil.convert(alu, ports=alu.ports())
144 with open("branch_pipeline.il", "w") as f:
145 f.write(vl)
146
147
148 class TestRunner(FHDLTestCase):
149 def __init__(self, test_data):
150 super().__init__("run_all")
151 self.test_data = test_data
152
153 def run_all(self):
154 m = Module()
155 comb = m.d.comb
156 instruction = Signal(32)
157
158 pdecode = create_pdecode()
159
160 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
161
162 pspec = BranchPipeSpec(id_wid=2)
163 m.submodules.branch = branch = BranchBasePipe(pspec)
164
165 comb += branch.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
166 comb += branch.p.valid_i.eq(1)
167 comb += branch.n.ready_i.eq(1)
168 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
169 sim = Simulator(m)
170
171 sim.add_clock(1e-6)
172
173 def process():
174 for test in self.test_data:
175 print(test.name)
176 program = test.program
177 self.subTest(test.name)
178 simulator = ISA(pdecode2, test.regs, test.sprs, test.cr,
179 test.mem, test.msr,
180 bigendian=bigendian)
181 initial_cia = 0x2000
182 simulator.set_pc(initial_cia)
183 gen = program.generate_instructions()
184 instructions = list(zip(gen, program.assembly.splitlines()))
185
186 pc = simulator.pc.CIA.value
187 msr = simulator.msr.value
188 index = (pc - initial_cia)//4
189 while index < len(instructions) and index >= 0:
190 print(index)
191 ins, code = instructions[index]
192
193 print("0x{:X}".format(ins & 0xffffffff))
194 print(code)
195
196 # ask the decoder to decode this binary data (endian'd)
197 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
198 yield pdecode2.msr.eq(msr) # set MSR in pdecode2
199 yield pdecode2.cia.eq(pc) # set PC in pdecode2
200 yield instruction.eq(ins) # raw binary instr.
201 # note, here, the op will need further decoding in order
202 # to set the correct SPRs on SPR1/2/3. op_bc* require
203 # spr1 to be set to CTR, op_bctar require spr2 to be
204 # set to TAR, op_bclr* require spr2 to be set to LR.
205 # if op_sc*, op_rf* and op_hrfid are to be added here
206 # then additional op-decoding is required, accordingly
207 yield Settle()
208 lk = yield pdecode2.e.do.lk
209 print("lk:", lk)
210 yield from self.set_inputs(branch, pdecode2, simulator)
211 fn_unit = yield pdecode2.e.do.fn_unit
212 self.assertEqual(fn_unit, Function.BRANCH.value, code)
213 yield
214 yield
215 opname = code.split(' ')[0]
216 prev_nia = simulator.pc.NIA.value
217 yield from simulator.call(opname)
218 pc = simulator.pc.CIA.value
219 msr = simulator.msr.value
220 index = (pc - initial_cia)//4
221
222 yield from self.assert_outputs(branch, pdecode2,
223 simulator, prev_nia, code)
224
225 sim.add_sync_process(process)
226 with sim.write_vcd("branch_simulator.vcd"):
227 sim.run()
228
229 def assert_outputs(self, branch, dec2, sim, prev_nia, code):
230 branch_taken = yield branch.n.data_o.nia.ok
231 sim_branch_taken = prev_nia != sim.pc.CIA
232 self.assertEqual(branch_taken, sim_branch_taken, code)
233 if branch_taken:
234 branch_addr = yield branch.n.data_o.nia.data
235 print(f"real: {branch_addr:x}, sim: {sim.pc.CIA.value:x}")
236 self.assertEqual(branch_addr, sim.pc.CIA.value, code)
237
238 # TODO: check write_fast1 as well (should contain CTR)
239
240 # TODO: this should be checking write_fast2
241 lk = yield dec2.e.do.lk
242 branch_lk = yield branch.n.data_o.lr.ok
243 self.assertEqual(lk, branch_lk, code)
244 if lk:
245 branch_lr = yield branch.n.data_o.lr.data
246 self.assertEqual(sim.spr['LR'], branch_lr, code)
247
248 def set_inputs(self, branch, dec2, sim):
249 print(f"cr0: {sim.crl[0].get_range()}")
250
251 inp = yield from get_cu_inputs(dec2, sim)
252
253 yield from ALUHelpers.set_fast_spr1(branch, dec2, inp)
254 yield from ALUHelpers.set_fast_spr2(branch, dec2, inp)
255 yield from ALUHelpers.set_cr_a(branch, dec2, inp)
256
257
258 if __name__ == "__main__":
259 unittest.main(exit=False)
260 suite = unittest.TestSuite()
261 suite.addTest(TestRunner(BranchTestCase.test_data))
262
263 runner = unittest.TextTestRunner()
264 runner.run(suite)