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