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