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