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