add mfmsr trap tests
[soc.git] / src / soc / fu / trap / 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, CryIn)
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.config.endian import bigendian
14 from soc.consts import MSR
15
16 from soc.fu.test.common import (TestCase, ALUHelpers)
17 from soc.fu.trap.pipeline import TrapBasePipe
18 from soc.fu.trap.pipe_data import TrapPipeSpec
19 import random
20
21
22 def get_cu_inputs(dec2, sim):
23 """naming (res) must conform to TrapFunctionUnit input regspec
24 """
25 res = {}
26
27 yield from ALUHelpers.get_sim_int_ra(res, sim, dec2) # RA
28 yield from ALUHelpers.get_sim_int_rb(res, sim, dec2) # RB
29 yield from ALUHelpers.get_sim_fast_spr1(res, sim, dec2) # SPR1
30 yield from ALUHelpers.get_sim_fast_spr2(res, sim, dec2) # SPR2
31 ALUHelpers.get_sim_cia(res, sim, dec2) # PC
32 ALUHelpers.get_sim_msr(res, sim, dec2) # MSR
33
34 print ("alu get_cu_inputs", res)
35
36 return res
37
38
39
40 def set_alu_inputs(alu, dec2, sim):
41 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
42 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
43 # and place it into data_i.b
44
45 inp = yield from get_cu_inputs(dec2, sim)
46 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
47 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
48 yield from ALUHelpers.set_fast_spr1(alu, dec2, inp) # SPR1
49 yield from ALUHelpers.set_fast_spr2(alu, dec2, inp) # SPR1
50
51 yield from ALUHelpers.set_cia(alu, dec2, inp)
52 yield from ALUHelpers.set_msr(alu, dec2, inp)
53 return inp
54
55 # This test bench is a bit different than is usual. Initially when I
56 # was writing it, I had all of the tests call a function to create a
57 # device under test and simulator, initialize the dut, run the
58 # simulation for ~2 cycles, and assert that the dut output what it
59 # should have. However, this was really slow, since it needed to
60 # create and tear down the dut and simulator for every test case.
61
62 # Now, instead of doing that, every test case in TrapTestCase puts some
63 # data into the test_data list below, describing the instructions to
64 # be tested and the initial state. Once all the tests have been run,
65 # test_data gets passed to TestRunner which then sets up the DUT and
66 # simulator once, runs all the data through it, and asserts that the
67 # results match the pseudocode sim at every cycle.
68
69 # By doing this, I've reduced the time it takes to run the test suite
70 # massively. Before, it took around 1 minute on my computer, now it
71 # takes around 3 seconds
72
73
74 class TrapTestCase(FHDLTestCase):
75 test_data = []
76
77 def __init__(self, name):
78 super().__init__(name)
79 self.test_name = name
80
81 def run_tst_program(self, prog, initial_regs=None, initial_sprs=None,
82 initial_msr=0):
83 tc = TestCase(prog, self.test_name, initial_regs, initial_sprs,
84 msr=initial_msr)
85 self.test_data.append(tc)
86
87 def test_1_rfid(self):
88 lst = ["rfid"]
89 initial_regs = [0] * 32
90 initial_regs[1] = 1
91 initial_sprs = {'SRR0': 0x12345678, 'SRR1': 0x5678}
92 self.run_tst_program(Program(lst, bigendian),
93 initial_regs, initial_sprs)
94
95 def test_0_trap_eq_imm(self):
96 insns = ["twi", "tdi"]
97 for i in range(2):
98 choice = random.choice(insns)
99 lst = [f"{choice} 4, 1, %d" % i] # TO=4: trap equal
100 initial_regs = [0] * 32
101 initial_regs[1] = 1
102 self.run_tst_program(Program(lst, bigendian), initial_regs)
103
104 def test_0_trap_eq(self):
105 insns = ["tw", "td"]
106 for i in range(2):
107 choice = insns[i]
108 lst = [f"{choice} 4, 1, 2"] # TO=4: trap equal
109 initial_regs = [0] * 32
110 initial_regs[1] = 1
111 initial_regs[2] = 1
112 self.run_tst_program(Program(lst, bigendian), initial_regs)
113
114
115 def test_3_mtmsr_0(self):
116 lst = ["mtmsr 1,0"]
117 initial_regs = [0] * 32
118 initial_regs[1] = 0xffffffffffffffff
119 self.run_tst_program(Program(lst, bigendian), initial_regs)
120
121 def test_3_mtmsr_1(self):
122 lst = ["mtmsr 1,1"]
123 initial_regs = [0] * 32
124 initial_regs[1] = 0xffffffffffffffff
125 self.run_tst_program(Program(lst, bigendian), initial_regs)
126
127 def test_4_mtmsrd_0(self):
128 lst = ["mtmsrd 1,0"]
129 initial_regs = [0] * 32
130 initial_regs[1] = 0xffffffffffffffff
131 self.run_tst_program(Program(lst, bigendian), initial_regs)
132
133 def test_5_mtmsrd_1(self):
134 lst = ["mtmsrd 1,1"]
135 initial_regs = [0] * 32
136 initial_regs[1] = 0xffffffffffffffff
137 self.run_tst_program(Program(lst, bigendian), initial_regs)
138
139 def test_6_mtmsr_priv_0(self):
140 lst = ["mtmsr 1,0"]
141 initial_regs = [0] * 32
142 initial_regs[1] = 0xffffffffffffffff
143 msr = 1 << MSR.PR # set in "problem state"
144 self.run_tst_program(Program(lst, bigendian), initial_regs,
145 initial_msr=msr)
146 def test_7_rfid_priv_0(self):
147 lst = ["rfid"]
148 initial_regs = [0] * 32
149 initial_regs[1] = 1
150 initial_sprs = {'SRR0': 0x12345678, 'SRR1': 0x5678}
151 msr = 1 << MSR.PR # set in "problem state"
152 self.run_tst_program(Program(lst, bigendian),
153 initial_regs, initial_sprs,
154 initial_msr=msr)
155
156 def test_8_mfmsr(self):
157 lst = ["mfmsr 1"]
158 initial_regs = [0] * 32
159 msr = (~(1 << MSR.PR)) & 0xffffffffffffffff
160 self.run_tst_program(Program(lst, bigendian), initial_regs,
161 initial_msr=msr)
162
163 def test_9_mfmsr_priv(self):
164 lst = ["mfmsr 1"]
165 initial_regs = [0] * 32
166 msr = 1 << MSR.PR # set in "problem state"
167 self.run_tst_program(Program(lst, bigendian), initial_regs,
168 initial_msr=msr)
169
170 def test_999_illegal(self):
171 # ok, um this is a bit of a cheat: use an instruction we know
172 # is not implemented by either ISACaller or the core
173 lst = ["tbegin.",
174 "mtmsr 1,1"] # should not get executed
175 initial_regs = [0] * 32
176 self.run_tst_program(Program(lst, bigendian), initial_regs)
177
178 def test_ilang(self):
179 pspec = TrapPipeSpec(id_wid=2)
180 alu = TrapBasePipe(pspec)
181 vl = rtlil.convert(alu, ports=alu.ports())
182 with open("trap_pipeline.il", "w") as f:
183 f.write(vl)
184
185
186 class TestRunner(FHDLTestCase):
187 def __init__(self, test_data):
188 super().__init__("run_all")
189 self.test_data = test_data
190
191 def run_all(self):
192 m = Module()
193 comb = m.d.comb
194 instruction = Signal(32)
195
196 pdecode = create_pdecode()
197
198 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
199
200 pspec = TrapPipeSpec(id_wid=2)
201 m.submodules.alu = alu = TrapBasePipe(pspec)
202
203 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
204 comb += alu.p.valid_i.eq(1)
205 comb += alu.n.ready_i.eq(1)
206 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
207 sim = Simulator(m)
208
209 sim.add_clock(1e-6)
210 def process():
211 for test in self.test_data:
212 print(test.name)
213 program = test.program
214 self.subTest(test.name)
215 sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
216 test.mem, test.msr,
217 bigendian=bigendian)
218 gen = program.generate_instructions()
219 instructions = list(zip(gen, program.assembly.splitlines()))
220
221 pc = sim.pc.CIA.value
222 index = pc//4
223 while index < len(instructions):
224 ins, code = instructions[index]
225
226 print("pc %08x instr: %08x" % (pc, ins & 0xffffffff))
227 print(code)
228 if 'XER' in sim.spr:
229 so = 1 if sim.spr['XER'][XER_bits['SO']] else 0
230 ov = 1 if sim.spr['XER'][XER_bits['OV']] else 0
231 ov32 = 1 if sim.spr['XER'][XER_bits['OV32']] else 0
232 print ("before: so/ov/32", so, ov, ov32)
233
234 # ask the decoder to decode this binary data (endian'd)
235 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
236 yield instruction.eq(ins) # raw binary instr.
237 yield Settle()
238 fn_unit = yield pdecode2.e.do.fn_unit
239 self.assertEqual(fn_unit, Function.TRAP.value)
240 alu_o = yield from set_alu_inputs(alu, pdecode2, sim)
241 yield pdecode2.msr.eq(alu_o['msr']) # set MSR in pdecode2
242 yield
243 opname = code.split(' ')[0]
244 yield from sim.call(opname)
245 pc = sim.pc.CIA.value
246 index = pc//4
247 print("pc after %08x" % (pc))
248
249 vld = yield alu.n.valid_o
250 while not vld:
251 yield
252 vld = yield alu.n.valid_o
253 yield
254
255 yield from self.check_alu_outputs(alu, pdecode2, sim, code)
256
257 sim.add_sync_process(process)
258 with sim.write_vcd("alu_simulator.vcd", "simulator.gtkw",
259 traces=[]):
260 sim.run()
261
262 def check_alu_outputs(self, alu, dec2, sim, code):
263
264 rc = yield dec2.e.do.rc.data
265 cridx_ok = yield dec2.e.write_cr.ok
266 cridx = yield dec2.e.write_cr.data
267
268 print ("check extra output", repr(code), cridx_ok, cridx)
269 if rc:
270 self.assertEqual(cridx, 0, code)
271
272 sim_o = {}
273 res = {}
274
275 yield from ALUHelpers.get_int_o(res, alu, dec2)
276 yield from ALUHelpers.get_fast_spr1(res, alu, dec2)
277 yield from ALUHelpers.get_fast_spr2(res, alu, dec2)
278 yield from ALUHelpers.get_nia(res, alu, dec2)
279 yield from ALUHelpers.get_msr(res, alu, dec2)
280
281 print ("output", res)
282
283 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
284 yield from ALUHelpers.get_wr_fast_spr1(sim_o, sim, dec2)
285 yield from ALUHelpers.get_wr_fast_spr2(sim_o, sim, dec2)
286 ALUHelpers.get_sim_nia(sim_o, sim, dec2)
287 ALUHelpers.get_sim_msr(sim_o, sim, dec2)
288
289 print ("sim output", sim_o)
290
291 ALUHelpers.check_int_o(self, res, sim_o, code)
292 ALUHelpers.check_fast_spr1(self, res, sim_o, code)
293 ALUHelpers.check_fast_spr2(self, res, sim_o, code)
294 ALUHelpers.check_nia(self, res, sim_o, code)
295 ALUHelpers.check_msr(self, res, sim_o, code)
296
297
298 if __name__ == "__main__":
299 unittest.main(exit=False)
300 suite = unittest.TestSuite()
301 suite.addTest(TestRunner(TrapTestCase.test_data))
302
303 runner = unittest.TextTestRunner()
304 runner.run(suite)