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