1 """TestRunner class, runs TestIssuer instructions
5 * https://bugs.libre-soc.org/show_bug.cgi?id=363
6 * https://bugs.libre-soc.org/show_bug.cgi?id=686#c51
8 from nmigen
import Module
, Signal
9 from nmigen
.hdl
.xfrm
import ResetInserter
11 from pprint
import pprint
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
17 from openpower
.decoder
.isa
.caller
import SVP64State
18 from openpower
.decoder
.isa
.all
import ISA
19 from openpower
.endian
import bigendian
21 from soc
.simple
.issuer
import TestIssuerInternal
22 from soc
.simple
.inorder
import TestIssuerInternalInOrder
24 from soc
.simple
.test
.test_core
import (setup_regs
, check_regs
, check_mem
,
27 from soc
.fu
.compunits
.test
.test_compunit
import (setup_tst_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
35 def insert_into_rom(startaddr
, instructions
, rom
):
36 print("insn before, init rom", len(instructions
))
39 startaddr
//= 4 # instructions are 32-bit
43 for ins
in instructions
:
44 if isinstance(ins
, tuple):
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
))
53 val
= rom
.get(msbs
<<3, 0)
55 print("before set", hex(4*startaddr
),
56 hex(msbs
), hex(val
), hex(insn
))
57 val
= (val |
(insn
<< (lsb
*32)))
61 print("after set", hex(4*startaddr
), hex(msbs
), hex(val
))
62 print("instr: %06x 0x%x %s %08x" % (4*startaddr
, insn
, code
, val
))
64 startaddr
= startaddr
& mask
66 print ("after insn insert")
70 def setup_i_memory(imem
, startaddr
, instructions
, rom
):
72 print("insn before, init mem", mem
.depth
, mem
.width
, mem
,
76 # initialise mem array to zero
77 for i
in range(mem
.depth
):
78 yield mem
._array
[i
].eq(0)
81 startaddr
//= 4 # instructions are 32-bit
83 assert rom
is None, "cannot do 32-bit from wb_get ROM yet"
85 for ins
in instructions
:
86 if isinstance(ins
, tuple):
90 insn
= insn
& 0xffffffff
91 yield mem
._array
[startaddr
].eq(insn
)
94 print("instr: %06x 0x%x %s" % (4*startaddr
, insn
, code
))
96 startaddr
= startaddr
& mask
101 for ins
in instructions
:
102 if isinstance(ins
, tuple):
106 insn
= insn
& 0xffffffff
107 msbs
= (startaddr
>> 1) & mask
108 lsb
= 1 if (startaddr
& 1) else 0
110 if rom
: # must put the value into the wb_get area
113 val
= yield mem
._array
[msbs
]
115 print("before set", hex(4*startaddr
),
116 hex(msbs
), hex(val
), hex(insn
))
117 val
= (val |
(insn
<< (lsb
*32)))
119 if rom
: # must put the value into the wb_get area
122 yield mem
._array
[msbs
].eq(val
)
125 print("after set", hex(4*startaddr
), hex(msbs
), hex(val
))
126 print("instr: %06x 0x%x %s %08x" % (4*startaddr
, insn
, code
, val
))
128 startaddr
= startaddr
& mask
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
)
137 ack
= yield dmi
.ack_o
142 yield dmi
.req_i
.eq(0)
143 yield dmi
.addr_i
.eq(0)
149 def get_dmi(dmi
, addr
):
150 yield dmi
.req_i
.eq(1)
151 yield dmi
.addr_i
.eq(addr
)
155 ack
= yield dmi
.ack_o
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)
168 class HDLRunner(StateRunner
):
169 """HDLRunner: Implements methods for the setup, preparation, and
170 running of tests using nmigen HDL simulation.
173 def __init__(self
, dut
, m
, pspec
):
174 super().__init
__("hdl", HDLRunner
)
178 self
.pc_i
= Signal(32)
179 self
.svstate_i
= Signal(64)
181 #hard_reset = Signal(reset_less=True)
183 self
.issuer
= TestIssuerInternalInOrder(pspec
)
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
193 comb
+= self
.issuer
.pc_i
.data
.eq(self
.pc_i
)
194 comb
+= self
.issuer
.svstate_i
.data
.eq(self
.svstate_i
)
196 def prepare_for_test(self
, test
):
198 #print ("preparing for test name", test.name)
200 # set up bigendian (TODO: don't do this, use MSR)
201 yield self
.issuer
.core_bigendian_i
.eq(bigendian
)
208 #print ("end of test preparation", test.name)
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)
215 yield self
.issuer
.dbg_rst_i
.eq(0)
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
)
224 def run_test(self
, instructions
):
225 """run_hdl_state - runs a TestIssuer nmigen HDL simulation
228 #print("starting test")
230 if self
.dut
.rom
is None:
231 imem
= self
.issuer
.imem
._get
_memory
()
232 #print("got memory", imem)
234 print("skipping memory get due to rom")
236 core
= self
.issuer
.core
237 dmi
= self
.issuer
.dbg
.dmi
238 pdecode2
= self
.issuer
.pdecode2
242 # establish the TestIssuer context (mem, regs etc)
244 pc
= 0 # start address
245 counter
= 0 # test to pause/start
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
)
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")
259 yield self
.pc_i
.eq(pc
)
260 yield self
.issuer
.pc_i
.ok
.eq(1)
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)
270 print("instructions", instructions
)
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
)
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
]
286 print("hdl instr: 0x{:X}".format(ins
& 0xffffffff))
292 yield from set_dmi(dmi
, DBGCore
.CTRL
,
294 yield self
.issuer
.pc_i
.ok
.eq(0) # no change PC after this
295 yield self
.issuer
.svstate_i
.ok
.eq(0) # ditto
299 counter
= counter
+ 1
301 # wait until executed
302 while not ((yield self
.issuer
.insn_done
) or
303 (yield self
.issuer
.dbg
.terminated_o
)):
306 # okaaay long story: in overlap mode, PC is updated one cycle
308 if self
.dut
.allow_overlap
:
312 index
= (yield self
.issuer
.cur_state
.pc
) // 4
314 terminated
= yield self
.issuer
.dbg
.terminated_o
315 print("terminated", terminated
, index
, len(instructions
))
317 if index
< len(instructions
):
318 # Get HDL mem and state
319 state
= yield from TestState("hdl", core
, self
.dut
,
321 hdl_states
.append(state
)
323 if index
>= len(instructions
):
324 print("index over, send dmi stop")
326 yield from set_dmi(dmi
, DBGCore
.CTRL
, 1 << DBGCtrl
.STOP
)
329 # hmm really should use DMI status check here but hey it's quick
331 stopped
= yield self
.issuer
.dbg
.core_stop_o
337 terminated
= yield self
.issuer
.dbg
.terminated_o
338 print("terminated(2)", terminated
)
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
):
349 if self
.dut
.allow_overlap
:
350 # get last state, at end of run
351 state
= yield from TestState("hdl", core
, self
.dut
,
353 hdl_states
.append(state
)
358 yield from set_dmi(self
.dmi
, DBGCore
.CTRL
, 1 << DBGCtrl
.STOP
)
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<<<)
369 cr
= yield from get_dmi(self
.dmi
, DBGCore
.CR
)
370 print("after test %s cr value %x" % (self
.test
.name
, cr
))
373 xer
= yield from get_dmi(self
.dmi
, DBGCore
.XER
)
374 print("after test %s XER value %x" % (self
.test
.name
, xer
))
377 msr
= yield from get_dmi(self
.dmi
, DBGCore
.MSR
)
378 print("after test %s MSR value %x" % (self
.test
.name
, msr
))
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
)
385 print("after test %s reg %2d value %x" %
386 (self
.test
.name
, int_reg
, value
))
389 yield from set_dmi(self
.dmi
, DBGCore
.CTRL
, 1 << DBGCtrl
.RESET
)
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):
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
)