Allow the formal engine to perform a same-cycle result in the ALU
[soc.git] / src / soc / simple / test / test_runner.py
1 """TestRunner class, runs TestIssuer instructions
2
3 related bugs:
4
5 * https://bugs.libre-soc.org/show_bug.cgi?id=363
6 * https://bugs.libre-soc.org/show_bug.cgi?id=686#c51
7 """
8 from nmigen import Module, Signal
9 from nmigen.hdl.xfrm import ResetInserter
10 from copy import copy
11 from pprint import pprint
12
13 # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell
14 # Also, check out the cxxsim nmigen branch, and latest yosys from git
15 from nmutil.sim_tmp_alternative import Simulator, Settle
16
17 from openpower.decoder.isa.caller import SVP64State
18 from openpower.decoder.isa.all import ISA
19 from openpower.endian import bigendian
20
21 from soc.simple.issuer import TestIssuerInternal
22 from soc.simple.inorder import TestIssuerInternalInOrder
23
24 from soc.simple.test.test_core import (setup_regs, check_regs, check_mem,
25 wait_for_busy_clear,
26 wait_for_busy_hi)
27 from soc.fu.compunits.test.test_compunit import (setup_tst_memory,
28 check_sim_memory)
29 from soc.debug.dmi import DBGCore, DBGCtrl, DBGStat
30 from nmutil.util import wrap
31 from openpower.test.state import TestState, StateRunner
32 from openpower.test.runner import TestRunnerBase
33
34
35 def insert_into_rom(startaddr, instructions, rom):
36 print("insn before, init rom", len(instructions))
37 pprint(rom)
38
39 startaddr //= 4 # instructions are 32-bit
40
41 # 64 bit
42 mask = ((1 << 64)-1)
43 for ins in instructions:
44 if isinstance(ins, tuple):
45 insn, code = ins
46 else:
47 insn, code = ins, ''
48 insn = insn & 0xffffffff
49 msbs = (startaddr >> 1) & mask
50 lsb = 1 if (startaddr & 1) else 0
51 print ("insn", hex(insn), hex(msbs), hex(lsb))
52
53 val = rom.get(msbs<<3, 0)
54 if insn != 0:
55 print("before set", hex(4*startaddr),
56 hex(msbs), hex(val), hex(insn))
57 val = (val | (insn << (lsb*32)))
58 val = val & mask
59 rom[msbs<<3] = val
60 if insn != 0:
61 print("after set", hex(4*startaddr), hex(msbs), hex(val))
62 print("instr: %06x 0x%x %s %08x" % (4*startaddr, insn, code, val))
63 startaddr += 1
64 startaddr = startaddr & mask
65
66 print ("after insn insert")
67 pprint(rom)
68
69
70 def setup_i_memory(imem, startaddr, instructions, rom):
71 mem = imem
72 print("insn before, init mem", mem.depth, mem.width, mem,
73 len(instructions))
74
75 if not rom:
76 # initialise mem array to zero
77 for i in range(mem.depth):
78 yield mem._array[i].eq(0)
79 yield Settle()
80
81 startaddr //= 4 # instructions are 32-bit
82 if mem.width == 32:
83 assert rom is None, "cannot do 32-bit from wb_get ROM yet"
84 mask = ((1 << 32)-1)
85 for ins in instructions:
86 if isinstance(ins, tuple):
87 insn, code = ins
88 else:
89 insn, code = ins, ''
90 insn = insn & 0xffffffff
91 yield mem._array[startaddr].eq(insn)
92 yield Settle()
93 if insn != 0:
94 print("instr: %06x 0x%x %s" % (4*startaddr, insn, code))
95 startaddr += 1
96 startaddr = startaddr & mask
97 return
98
99 # 64 bit
100 mask = ((1 << 64)-1)
101 for ins in instructions:
102 if isinstance(ins, tuple):
103 insn, code = ins
104 else:
105 insn, code = ins, ''
106 insn = insn & 0xffffffff
107 msbs = (startaddr >> 1) & mask
108 lsb = 1 if (startaddr & 1) else 0
109
110 if rom: # must put the value into the wb_get area
111 val = rom[msbs<<1]
112 else:
113 val = yield mem._array[msbs]
114 if insn != 0:
115 print("before set", hex(4*startaddr),
116 hex(msbs), hex(val), hex(insn))
117 val = (val | (insn << (lsb*32)))
118 val = val & mask
119 if rom: # must put the value into the wb_get area
120 rom[msbs<<1] = val
121 else:
122 yield mem._array[msbs].eq(val)
123 yield Settle()
124 if insn != 0:
125 print("after set", hex(4*startaddr), hex(msbs), hex(val))
126 print("instr: %06x 0x%x %s %08x" % (4*startaddr, insn, code, val))
127 startaddr += 1
128 startaddr = startaddr & mask
129
130
131 def set_dmi(dmi, addr, data):
132 yield dmi.req_i.eq(1)
133 yield dmi.addr_i.eq(addr)
134 yield dmi.din.eq(data)
135 yield dmi.we_i.eq(1)
136 while True:
137 ack = yield dmi.ack_o
138 if ack:
139 break
140 yield
141 yield
142 yield dmi.req_i.eq(0)
143 yield dmi.addr_i.eq(0)
144 yield dmi.din.eq(0)
145 yield dmi.we_i.eq(0)
146 yield
147
148
149 def get_dmi(dmi, addr):
150 yield dmi.req_i.eq(1)
151 yield dmi.addr_i.eq(addr)
152 yield dmi.din.eq(0)
153 yield dmi.we_i.eq(0)
154 while True:
155 ack = yield dmi.ack_o
156 if ack:
157 break
158 yield
159 yield # wait one
160 data = yield dmi.dout # get data after ack valid for 1 cycle
161 yield dmi.req_i.eq(0)
162 yield dmi.addr_i.eq(0)
163 yield dmi.we_i.eq(0)
164 yield
165 return data
166
167
168 class HDLRunner(StateRunner):
169 """HDLRunner: Implements methods for the setup, preparation, and
170 running of tests using nmigen HDL simulation.
171 """
172
173 def __init__(self, dut, m, pspec):
174 super().__init__("hdl", HDLRunner)
175
176 self.dut = dut
177 self.pspec = pspec
178 self.pc_i = Signal(32)
179 self.svstate_i = Signal(64)
180
181 #hard_reset = Signal(reset_less=True)
182 if pspec.inorder:
183 self.issuer = TestIssuerInternalInOrder(pspec)
184 else:
185 self.issuer = TestIssuerInternal(pspec)
186 # use DMI RESET command instead, this does actually work though
187 # issuer = ResetInserter({'coresync': hard_reset,
188 # 'sync': hard_reset})(issuer)
189 m.submodules.issuer = self.issuer
190 self.dmi = self.issuer.dbg.dmi
191
192 comb = m.d.comb
193 comb += self.issuer.pc_i.data.eq(self.pc_i)
194 comb += self.issuer.svstate_i.data.eq(self.svstate_i)
195
196 def prepare_for_test(self, test):
197 self.test = test
198 #print ("preparing for test name", test.name)
199
200 # set up bigendian (TODO: don't do this, use MSR)
201 yield self.issuer.core_bigendian_i.eq(bigendian)
202 yield Settle()
203
204 yield
205 yield
206 yield
207 yield
208 #print ("end of test preparation", test.name)
209
210 def setup_during_test(self):
211 # first run a manual hard-reset of the debug interface.
212 # core is counting down on a 3-clock delay at this point
213 yield self.issuer.dbg_rst_i.eq(1)
214 yield
215 yield self.issuer.dbg_rst_i.eq(0)
216
217 # now run a DMI-interface reset. because DMI is running
218 # in dbgsync domain its reset is *NOT* connected to
219 # core reset (hence the dbg_rst_i blip, above)
220 yield from set_dmi(self.dmi, DBGCore.CTRL, 1 << DBGCtrl.STOP)
221 yield
222 #print("test setup")
223
224 def run_test(self, instructions):
225 """run_hdl_state - runs a TestIssuer nmigen HDL simulation
226 """
227
228 #print("starting test")
229
230 if self.dut.rom is None:
231 imem = self.issuer.imem._get_memory()
232 #print("got memory", imem)
233 else:
234 print("skipping memory get due to rom")
235 pprint(self.dut.rom)
236 core = self.issuer.core
237 dmi = self.issuer.dbg.dmi
238 pdecode2 = self.issuer.pdecode2
239 l0 = core.l0
240 hdl_states = []
241
242 # establish the TestIssuer context (mem, regs etc)
243
244 pc = 0 # start address
245 counter = 0 # test to pause/start
246
247 # XXX for now, when ROM (run under wb_get) is detected,
248 # skip setup of memories. must be done a different way
249 if self.dut.rom is None:
250 yield from setup_i_memory(imem, pc, instructions, self.dut.rom)
251 yield from setup_tst_memory(l0, self.test.mem)
252 else:
253 insert_into_rom(pc, instructions, self.dut.default_mem)
254 print("about to setup regs")
255 yield from setup_regs(pdecode2, core, self.test)
256 #print("setup mem and regs done")
257
258 # set PC and SVSTATE
259 yield self.pc_i.eq(pc)
260 yield self.issuer.pc_i.ok.eq(1)
261
262 # copy initial SVSTATE
263 initial_svstate = copy(self.test.svstate)
264 if isinstance(initial_svstate, int):
265 initial_svstate = SVP64State(initial_svstate)
266 yield self.svstate_i.eq(initial_svstate.value)
267 yield self.issuer.svstate_i.ok.eq(1)
268 yield
269
270 print("instructions", instructions)
271
272 # before starting the simulation, set the core stop address to be
273 # just after the last instruction. if a load of an instruction is
274 # requested at this address, the core is immediately put into "halt"
275 # XXX: keep an eye out for in-order problems
276 hard_stop_addr = self.test.stop_at_pc
277 if hard_stop_addr is None:
278 hard_stop_addr = len(instructions)*4
279 yield from set_dmi(dmi, DBGCore.STOPADDR, hard_stop_addr)
280
281 # run the loop of the instructions on the current test
282 index = (yield self.issuer.cur_state.pc) // 4
283 while index < len(instructions):
284 ins, code = instructions[index]
285
286 print("hdl instr: 0x{:X}".format(ins & 0xffffffff))
287 print(index, code)
288
289 if counter == 0:
290 # start the core
291 yield
292 yield from set_dmi(dmi, DBGCore.CTRL,
293 1 << DBGCtrl.START)
294 yield self.issuer.pc_i.ok.eq(0) # no change PC after this
295 yield self.issuer.svstate_i.ok.eq(0) # ditto
296 yield
297 yield
298
299 counter = counter + 1
300
301 # wait until executed
302 while not ((yield self.issuer.insn_done) or
303 (yield self.issuer.dbg.terminated_o)):
304 yield
305
306 # okaaay long story: in overlap mode, PC is updated one cycle
307 # late.
308 if self.dut.allow_overlap:
309 yield
310 yield Settle()
311
312 index = (yield self.issuer.cur_state.pc) // 4
313
314 terminated = yield self.issuer.dbg.terminated_o
315 print("terminated", terminated, index, len(instructions))
316
317 if index < len(instructions):
318 # Get HDL mem and state
319 state = yield from TestState("hdl", core, self.dut,
320 code)
321 hdl_states.append(state)
322
323 if index >= len(instructions):
324 print("index over, send dmi stop")
325 # stop at end
326 yield from set_dmi(dmi, DBGCore.CTRL, 1 << DBGCtrl.STOP)
327 yield
328 yield
329 # hmm really should use DMI status check here but hey it's quick
330 while True:
331 stopped = yield self.issuer.dbg.core_stop_o
332 if stopped:
333 break
334 yield
335 break
336
337 terminated = yield self.issuer.dbg.terminated_o
338 print("terminated(2)", terminated)
339 if terminated:
340 break
341
342 if self.dut.allow_overlap: # or not self.dut.rom: ??
343 # wait until all settled
344 # XXX really this should be in DMI, which should in turn
345 # use issuer.any_busy to not send back "stopped" signal
346 while (yield self.issuer.any_busy):
347 yield
348
349 if self.dut.allow_overlap:
350 # get last state, at end of run
351 state = yield from TestState("hdl", core, self.dut,
352 code)
353 hdl_states.append(state)
354
355 return hdl_states
356
357 def end_test(self):
358 yield from set_dmi(self.dmi, DBGCore.CTRL, 1 << DBGCtrl.STOP)
359 yield
360 yield
361
362 # TODO, here is where the static (expected) results
363 # can be checked: register check (TODO, memory check)
364 # see https://bugs.libre-soc.org/show_bug.cgi?id=686#c51
365 # yield from check_regs(self, sim, core, test, code,
366 # >>>expected_data<<<)
367
368 # get CR
369 cr = yield from get_dmi(self.dmi, DBGCore.CR)
370 print("after test %s cr value %x" % (self.test.name, cr))
371
372 # get XER
373 xer = yield from get_dmi(self.dmi, DBGCore.XER)
374 print("after test %s XER value %x" % (self.test.name, xer))
375
376 # get MSR
377 msr = yield from get_dmi(self.dmi, DBGCore.MSR)
378 print("after test %s MSR value %x" % (self.test.name, msr))
379
380 # test of dmi reg get
381 for int_reg in range(32):
382 yield from set_dmi(self.dmi, DBGCore.GSPR_IDX, int_reg)
383 value = yield from get_dmi(self.dmi, DBGCore.GSPR_DATA)
384
385 print("after test %s reg %2d value %x" %
386 (self.test.name, int_reg, value))
387
388 # pull a reset
389 yield from set_dmi(self.dmi, DBGCore.CTRL, 1 << DBGCtrl.RESET)
390 yield
391
392
393 class TestRunner(TestRunnerBase):
394 def __init__(self, tst_data, microwatt_mmu=False, rom=None,
395 svp64=True, inorder=False, run_hdl=True, run_sim=True,
396 allow_overlap=False):
397 if run_hdl:
398 run_hdl = HDLRunner
399 super().__init__(tst_data, microwatt_mmu=microwatt_mmu,
400 rom=rom, inorder=inorder,
401 svp64=svp64, run_hdl=run_hdl, run_sim=run_sim,
402 allow_overlap=allow_overlap)