fix broken url
[openpower-isa.git] / src / openpower / test / runner.py
1 """TestRunner class, part of the Test API
2
3 SPDX-License: LGPLv2+
4
5 Copyright (C) 2020,2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
6 Copyright (C) 2021 Kyle Lehman <klehman9@comcast.net>
7 Copyright (C) 2021 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
8 Copyright (C) 2021 Tobias Platen <tplaten@posteo.de>
9 Copyright (C) 2021 Cesar Strauss <cestrauss@gmail.com>
10
11 related bugs:
12
13 * https://bugs.libre-soc.org/show_bug.cgi?id=363
14 * https://bugs.libre-soc.org/show_bug.cgi?id=686#c51
15 """
16
17 from nmigen import Module, ClockSignal
18 from copy import copy
19
20 # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell
21 # Also, check out the cxxsim nmigen branch, and latest yosys from git
22 from nmutil.sim_tmp_alternative import Simulator, Settle
23
24 from nmutil.formaltest import FHDLTestCase
25 from nmutil.gtkw import write_gtkw
26 from openpower.decoder.isa.all import ISA
27 from openpower.endian import bigendian
28
29 from openpower.decoder.power_decoder2 import PowerDecode2
30
31 from soc.config.test.test_loadstore import TestMemPspec
32 from nmutil.util import wrap
33 from openpower.test.wb_get import wb_get
34 from openpower.test.state import TestState, StateRunner, ExpectedState
35
36
37 class SimRunner(StateRunner):
38 """SimRunner: Implements methods for the setup, preparation, and
39 running of tests using ISACaller simulation
40 """
41 def __init__(self, dut, m, pspec):
42 super().__init__("sim", SimRunner)
43 self.dut = dut
44
45 self.mmu = pspec.mmu == True
46 regreduce_en = pspec.regreduce_en == True
47 self.simdec2 = simdec2 = PowerDecode2(None, regreduce_en=regreduce_en)
48 m.submodules.simdec2 = simdec2 # pain in the neck
49
50 def prepare_for_test(self, test):
51 self.test = test
52 if False:
53 yield
54
55 def run_test(self, instructions, gen, insncode):
56 """run_sim_state - runs an ISACaller simulation
57 """
58
59 dut, test, simdec2 = self.dut, self.test, self.simdec2
60 sim_states = []
61
62 # set up the Simulator (which must track TestIssuer exactly)
63 sim = ISA(simdec2, test.regs, test.sprs, test.cr, test.mem,
64 test.msr,
65 initial_insns=gen, respect_pc=True,
66 disassembly=insncode,
67 bigendian=bigendian,
68 initial_svstate=test.svstate,
69 mmu=self.mmu)
70
71 # run the loop of the instructions on the current test
72 index = sim.pc.CIA.value//4
73 while index < len(instructions):
74 ins, code = instructions[index]
75
76 print("sim instr: 0x{:X}".format(ins & 0xffffffff))
77 print(index, code)
78
79 # set up simulated instruction (in simdec2)
80 try:
81 yield from sim.setup_one()
82 except KeyError: # instruction not in imem: stop
83 break
84 yield Settle()
85
86 # call simulated operation
87 print("sim", code)
88 yield from sim.execute_one()
89 yield Settle()
90 index = sim.pc.CIA.value//4
91
92 # get sim register and memory TestState, add to list
93 state = yield from TestState("sim", sim, dut, code)
94 sim_states.append(state)
95
96 if self.dut.allow_overlap:
97 # get last state, at end of run
98 state = yield from TestState("sim", sim, dut, code)
99 sim_states.append(state)
100
101 return sim_states
102
103
104 class TestRunnerBase(FHDLTestCase):
105 """TestRunnerBase: Sets up and executes the running of tests
106 contained in tst_data. run_hdl (if provided) is an HDLRunner
107 object. If not provided, hdl simulation is skipped.
108
109 ISACaller simulation can be skipped by setting run_sim=False.
110
111 When using an Expected state to test with, the expected state
112 is passed in with tst_data.
113 """
114 def __init__(self, tst_data, microwatt_mmu=False, rom=None,
115 svp64=True, run_hdl=None, run_sim=True,
116 allow_overlap=False):
117 super().__init__("run_all")
118 self.test_data = tst_data
119 self.microwatt_mmu = microwatt_mmu
120 self.rom = rom
121 self.svp64 = svp64
122 self.allow_overlap = allow_overlap
123 self.run_hdl = run_hdl
124 self.run_sim = run_sim
125
126 def run_all(self):
127 m = Module()
128 comb = m.d.comb
129 if self.microwatt_mmu:
130 ldst_ifacetype = 'test_mmu_cache_wb'
131 else:
132 ldst_ifacetype = 'test_bare_wb'
133 imem_ifacetype = 'test_bare_wb'
134
135 pspec = TestMemPspec(ldst_ifacetype=ldst_ifacetype,
136 imem_ifacetype=imem_ifacetype,
137 addr_wid=48,
138 mask_wid=8,
139 imem_reg_wid=64,
140 # wb_data_width=32,
141 use_pll=False,
142 nocore=False,
143 xics=False,
144 gpio=False,
145 regreduce=not self.allow_overlap,
146 svp64=self.svp64,
147 allow_overlap=self.allow_overlap,
148 mmu=self.microwatt_mmu,
149 reg_wid=64)
150
151 ###### SETUP PHASE #######
152 # Determine the simulations needed and add to state_list
153 # for setup and running
154 # The methods contained in the respective Runner classes are
155 # called using this list when possible
156
157 state_list = []
158
159 if self.run_hdl:
160 hdlrun = self.run_hdl(self, m, pspec)
161 state_list.append(hdlrun)
162
163 if self.run_sim:
164 simrun = SimRunner(self, m, pspec)
165 state_list.append(simrun)
166
167 # run core clock at same rate as test clock
168 # XXX this has to stay here! TODO, work out why,
169 # but Simulation-only fails without it
170 intclk = ClockSignal("coresync")
171 comb += intclk.eq(ClockSignal())
172
173 # nmigen Simulation - everything runs around this, so it
174 # still has to be created.
175 sim = Simulator(m)
176 sim.add_clock(1e-6)
177
178 def process():
179
180 ###### PREPARATION PHASE AT START OF RUNNING #######
181
182 for runner in state_list:
183 yield from runner.setup_during_test()
184
185 # get each test, completely reset the core, and run it
186
187 for test in self.test_data:
188
189 with self.subTest(test.name):
190
191 ###### PREPARATION PHASE AT START OF TEST #######
192
193 for runner in state_list:
194 yield from runner.prepare_for_test(test)
195
196 print(test.name)
197 program = test.program
198 print("regs", test.regs)
199 print("sprs", test.sprs)
200 print("cr", test.cr)
201 print("mem", test.mem)
202 print("msr", test.msr)
203 print("assem", program.assembly)
204 gen = list(program.generate_instructions())
205 insncode = program.assembly.splitlines()
206 instructions = list(zip(gen, insncode))
207
208 ###### RUNNING OF EACH TEST #######
209 # StateRunner.step_test()
210
211 # Run two tests (TODO, move these to functions)
212 # * first the Simulator, collate a batch of results
213 # * then the HDL, likewise
214 # (actually, the other way round because running
215 # Simulator somehow modifies the test state!)
216 # * finally, compare all the results
217
218 # TODO https://bugs.libre-soc.org/show_bug.cgi?id=686#c73
219
220 ##########
221 # 1. HDL
222 ##########
223 if self.run_hdl:
224 hdl_states = yield from hdlrun.run_test(instructions)
225
226 ##########
227 # 2. Simulator
228 ##########
229
230 if self.run_sim:
231 sim_states = yield from simrun.run_test(
232 instructions, gen,
233 insncode)
234
235 ###### COMPARING THE TESTS #######
236
237 ###############
238 # 3. Compare
239 ###############
240
241 # TODO: here just grab one entry from list_of_sim_runners
242 # (doesn't matter which one, honestly)
243 # TODO https://bugs.libre-soc.org/show_bug.cgi?id=686#c73
244
245 if self.run_sim:
246 last_sim = copy(sim_states[-1])
247 elif self.run_hdl:
248 last_sim = copy(hdl_states[-1])
249 else:
250 last_sim = None # err what are you doing??
251
252 if self.run_hdl:
253 print ("hdl_states")
254 for state in hdl_states:
255 print (state)
256
257 if self.run_sim:
258 print ("sim_states")
259 for state in sim_states:
260 print (state)
261
262 # compare the states
263 if self.run_hdl and self.run_sim:
264 # if allow_overlap is enabled, because allow_overlap
265 # can commit out-of-order, only compare the last ones
266 if self.allow_overlap:
267 print ("allow_overlap: truncating %d %d "
268 "states to last" % (len(sim_states),
269 len(hdl_states)))
270 sim_states = sim_states[-1:]
271 hdl_states = hdl_states[-1:]
272 sim_states[-1].dump_state_tofile()
273 print ("allow_overlap: last hdl_state")
274 hdl_states[-1].dump_state_tofile()
275 for simstate, hdlstate in zip(sim_states, hdl_states):
276 simstate.compare(hdlstate) # register check
277 simstate.compare_mem(hdlstate) # memory check
278
279 # if no expected, create /tmp/case_name.py with code
280 # setting expected state to last_sim
281 if test.expected is None:
282 last_sim.dump_state_tofile(test.name, test.test_file)
283
284 # compare against expected results
285 if test.expected is not None:
286 # have to put these in manually
287 test.expected.to_test = test.expected
288 test.expected.dut = self
289 test.expected.state_type = "expected"
290 test.expected.code = 0
291 # do actual comparison, against last item
292 last_sim.compare(test.expected)
293
294 if self.run_hdl and self.run_sim:
295 self.assertTrue(len(hdl_states) == len(sim_states),
296 "number of instructions run not the same")
297
298 ###### END OF A TEST #######
299 # StateRunner.end_test()
300
301 for runner in state_list:
302 yield from runner.end_test() # TODO, some arguments?
303
304 ###### END OF EVERYTHING (but none needs doing, still call fn) ####
305 # StateRunner.cleanup()
306
307 for runner in state_list:
308 yield from runner.cleanup() # TODO, some arguments?
309
310 styles = {
311 'dec': {'base': 'dec'},
312 'bin': {'base': 'bin'},
313 'closed': {'closed': True}
314 }
315
316 traces = [
317 'clk',
318 ('state machines', 'closed', [
319 'fetch_pc_i_valid', 'fetch_pc_o_ready',
320 'fetch_fsm_state',
321 'fetch_insn_o_valid', 'fetch_insn_i_ready',
322 'pred_insn_i_valid', 'pred_insn_o_ready',
323 'fetch_predicate_state',
324 'pred_mask_o_valid', 'pred_mask_i_ready',
325 'issue_fsm_state',
326 'exec_insn_i_valid', 'exec_insn_o_ready',
327 'exec_fsm_state',
328 'exec_pc_o_valid', 'exec_pc_i_ready',
329 'insn_done', 'core_stop_o', 'pc_i_ok', 'pc_changed',
330 'is_last', 'dec2.no_out_vec']),
331 {'comment': 'fetch and decode'},
332 (None, 'dec', [
333 'cia[63:0]', 'nia[63:0]', 'pc[63:0]', 'msr[63:0]',
334 'cur_pc[63:0]', 'core_core_cia[63:0]']),
335 'raw_insn_i[31:0]',
336 'raw_opcode_in[31:0]', 'insn_type', 'dec2.dec2_exc_happened',
337 ('svp64 decoding', 'closed', [
338 'svp64_rm[23:0]', ('dec2.extra[8:0]', 'bin'),
339 'dec2.sv_rm_dec.mode', 'dec2.sv_rm_dec.predmode',
340 'dec2.sv_rm_dec.ptype_in',
341 'dec2.sv_rm_dec.dstpred[2:0]', 'dec2.sv_rm_dec.srcpred[2:0]',
342 'dstmask[63:0]', 'srcmask[63:0]',
343 'dregread[4:0]', 'dinvert',
344 'sregread[4:0]', 'sinvert',
345 'core.int.pred__addr[4:0]', 'core.int.pred__data_o[63:0]',
346 'core.int.pred__ren']),
347 ('register augmentation', 'dec', 'closed', [
348 {'comment': 'v3.0b registers'},
349 'dec2.dec_o.RT[4:0]',
350 'dec2.dec_a.RA[4:0]',
351 'dec2.dec_b.RB[4:0]',
352 ('Rdest', [
353 'dec2.o_svdec.reg_in[4:0]',
354 ('dec2.o_svdec.spec[2:0]', 'bin'),
355 'dec2.o_svdec.reg_out[6:0]']),
356 ('Rsrc1', [
357 'dec2.in1_svdec.reg_in[4:0]',
358 ('dec2.in1_svdec.spec[2:0]', 'bin'),
359 'dec2.in1_svdec.reg_out[6:0]']),
360 ('Rsrc1', [
361 'dec2.in2_svdec.reg_in[4:0]',
362 ('dec2.in2_svdec.spec[2:0]', 'bin'),
363 'dec2.in2_svdec.reg_out[6:0]']),
364 {'comment': 'SVP64 registers'},
365 'dec2.rego[6:0]', 'dec2.reg1[6:0]', 'dec2.reg2[6:0]'
366 ]),
367 {'comment': 'svp64 context'},
368 'core_core_vl[6:0]', 'core_core_maxvl[6:0]',
369 'core_core_srcstep[6:0]', 'next_srcstep[6:0]',
370 'core_core_dststep[6:0]',
371 {'comment': 'issue and execute'},
372 'core.core_core_insn_type',
373 (None, 'dec', [
374 'core_rego[6:0]', 'core_reg1[6:0]', 'core_reg2[6:0]']),
375 'issue_i', 'busy_o',
376 {'comment': 'dmi'},
377 'dbg.dmi_req_i', 'dbg.dmi_ack_o',
378 {'comment': 'instruction memory'},
379 'imem.sram.rdport.memory(0)[63:0]',
380 {'comment': 'registers'},
381 # match with soc.regfile.regfiles.IntRegs port names
382 'core.int.rp_src1.memory(0)[63:0]',
383 'core.int.rp_src1.memory(1)[63:0]',
384 'core.int.rp_src1.memory(2)[63:0]',
385 'core.int.rp_src1.memory(3)[63:0]',
386 'core.int.rp_src1.memory(4)[63:0]',
387 'core.int.rp_src1.memory(5)[63:0]',
388 'core.int.rp_src1.memory(6)[63:0]',
389 'core.int.rp_src1.memory(7)[63:0]',
390 'core.int.rp_src1.memory(9)[63:0]',
391 'core.int.rp_src1.memory(10)[63:0]',
392 'core.int.rp_src1.memory(13)[63:0]',
393 # Exceptions: see list archive for description of the chain
394 # http://lists.libre-soc.org/pipermail/libre-soc-dev/2021-December/004220.html
395 ('exceptions', 'closed', [
396 'exc_happened',
397 'pdecode2.exc_happened',
398 'core.exc_happened',
399 'core.fus.ldst0.exc_o_happened']),
400 ]
401
402 # PortInterface module path varies depending on MMU option
403 if self.microwatt_mmu:
404 pi_module = 'core.ldst0'
405 else:
406 pi_module = 'core.fus.ldst0'
407
408 traces += [('ld/st port interface', {'submodule': pi_module}, [
409 'oper_r__insn_type',
410 'oper_r__msr[63:0]',
411 'ldst_port0_is_ld_i',
412 'ldst_port0_is_st_i',
413 'ldst_port0_busy_o',
414 'ldst_port0_addr_i[47:0]',
415 'ldst_port0_addr_i_ok',
416 'ldst_port0_addr_ok_o',
417 'ldst_port0_exc_happened',
418 'ldst_port0_st_data_i[63:0]',
419 'ldst_port0_st_data_i_ok',
420 'ldst_port0_ld_data_o[63:0]',
421 'ldst_port0_ld_data_o_ok',
422 'ldst_port0_msr_pr',
423 'exc_o_happened',
424 'cancel'
425 ])]
426
427 if self.microwatt_mmu:
428 traces += [
429 {'comment': 'microwatt_mmu'},
430 'core.fus.mmu0.alu_mmu0.illegal',
431 'core.fus.mmu0.alu_mmu0.debug0[3:0]',
432 'core.fus.mmu0.alu_mmu0.mmu.state',
433 'core.fus.mmu0.alu_mmu0.mmu.pid[31:0]',
434 'core.fus.mmu0.alu_mmu0.mmu.prtbl[63:0]',
435 {'comment': 'wishbone_memory'},
436 'core.l0.pimem.bus__ack',
437 'core.l0.pimem.bus__adr[4:0]',
438 'core.l0.pimem.bus__bte',
439 'core.l0.pimem.bus__cti',
440 'core.l0.pimem.bus__cyc',
441 'core.l0.pimem.bus__dat_r[63:0]',
442 'core.l0.pimem.bus__dat_w[63:0]',
443 'core.l0.pimem.bus__dat_err',
444 'core.l0.pimem.bus__dat_sel[7:0]',
445 'core.l0.pimem.bus__dat_stb',
446 'core.l0.pimem.bus__dat_we',
447 ]
448
449 write_gtkw("issuer_simulator.gtkw",
450 "issuer_simulator.vcd",
451 traces, styles, module='top.issuer')
452
453 # add run of instructions
454 sim.add_sync_process(process)
455
456 # ARGH oh whoops. TODO, core is not passed in!
457 # urrr... work out a hacky-way to sort this (access run_hdl
458 # core directly for now)
459
460 # optionally, if a wishbone-based ROM is passed in, run that as an
461 # extra emulated process
462 if self.rom is not None:
463 dcache = hdlrun.issuer.core.fus.fus["mmu0"].alu.dcache
464 default_mem = self.rom
465 sim.add_sync_process(wrap(wb_get(dcache.bus,
466 default_mem, "DCACHE")))
467
468 with sim.write_vcd("issuer_simulator.vcd"):
469 sim.run()