11 # Note that gdb comes with its own testsuite. I was unable to figure out how to
12 # run that testsuite against the spike simulator.
15 for directory
in (os
.getcwd(), os
.path
.dirname(__file__
)):
16 fullpath
= os
.path
.join(directory
, path
)
17 if os
.path
.exists(fullpath
):
21 def compile(args
, xlen
=32): # pylint: disable=redefined-builtin
22 cc
= os
.path
.expandvars("$RISCV/bin/riscv%d-unknown-elf-gcc" % xlen
)
25 found
= find_file(arg
)
31 result
= os
.system(cmd
)
32 assert result
== 0, "%r failed" % cmd
35 # http://stackoverflow.com/questions/2838244/get-open-tcp-port-in-python/2838309#2838309
37 s
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
39 port
= s
.getsockname()[1]
46 def __init__(self
, cmd
, binary
=None, halted
=False, with_gdb
=True,
47 timeout
=None, xlen
=64):
48 """Launch spike. Return tuple of its process and the port it's running
51 cmd
= shlex
.split(cmd
)
55 cmd
+= ["--isa", "RV32"]
58 cmd
= ["timeout", str(timeout
)] + cmd
63 self
.port
= unused_port()
64 cmd
+= ['--gdb-port', str(self
.port
)]
69 logfile
= open(self
.logname
, "w")
70 logfile
.write("+ %s\n" % " ".join(cmd
))
72 self
.process
= subprocess
.Popen(cmd
, stdin
=subprocess
.PIPE
,
73 stdout
=logfile
, stderr
=logfile
)
82 def wait(self
, *args
, **kwargs
):
83 return self
.process
.wait(*args
, **kwargs
)
86 def __init__(self
, simv
=None, debug
=False):
88 cmd
= shlex
.split(simv
)
91 cmd
+= ["+jtag_vpi_enable"]
93 cmd
[0] = cmd
[0] + "-debug"
94 cmd
+= ["+vcdplusfile=output/gdbserver.vpd"]
95 logfile
= open("simv.log", "w")
96 logfile
.write("+ %s\n" % " ".join(cmd
))
98 listenfile
= open("simv.log", "r")
100 self
.process
= subprocess
.Popen(cmd
, stdin
=subprocess
.PIPE
,
101 stdout
=logfile
, stderr
=logfile
)
104 line
= listenfile
.readline()
107 match
= re
.match(r
"^Listening on port (\d+)$", line
)
110 self
.port
= int(match
.group(1))
111 print "Using port %d for JTAG VPI" % self
.port
120 class Openocd(object):
121 logname
= "openocd.log"
123 def __init__(self
, cmd
=None, config
=None, debug
=False):
125 cmd
= shlex
.split(cmd
)
129 cmd
+= ["-f", find_file(config
)]
134 self
.port
= unused_port()
135 print "Using port %d for gdb server" % self
.port
136 # This command needs to come before any config scripts on the command
137 # line, since they are executed in order.
138 cmd
[1:1] = ["--command", "gdb_port %d" % self
.port
]
140 env
= os
.environ
.copy()
141 env
['JTAG_VPI_PORT'] = str(otherProcess
.port
)
143 logfile
= open(Openocd
.logname
, "w")
144 logfile
.write("+ %s\n" % " ".join(cmd
))
146 self
.process
= subprocess
.Popen(cmd
, stdin
=subprocess
.PIPE
,
147 stdout
=logfile
, stderr
=logfile
, env
=env
)
149 # Wait for OpenOCD to have made it through riscv_examine(). When using
150 # OpenOCD to communicate with a simulator this may take a long time,
151 # and gdb will time out when trying to connect if we attempt too early.
155 log
= open(Openocd
.logname
).read()
156 if "Examined RISCV core" in log
:
158 if not self
.process
.poll() is None:
160 "OpenOCD exited before completing riscv_examine()")
161 if not messaged
and time
.time() - start
> 1:
163 print "Waiting for OpenOCD to examine RISCV core..."
172 class OpenocdCli(object):
173 def __init__(self
, port
=4444):
174 self
.child
= pexpect
.spawn("sh -c 'telnet localhost %d | tee openocd-cli.log'" % port
)
175 self
.child
.expect("> ")
177 def command(self
, cmd
):
178 self
.child
.sendline(cmd
)
179 self
.child
.expect("\n")
180 self
.child
.expect("> ")
181 return self
.child
.before
.strip()
183 class CannotAccess(Exception):
184 def __init__(self
, address
):
185 Exception.__init
__(self
)
186 self
.address
= address
190 cmd
=os
.path
.expandvars("$RISCV/bin/riscv64-unknown-elf-gdb")):
191 self
.child
= pexpect
.spawn(cmd
)
192 self
.child
.logfile
= open("gdb.log", "w")
193 self
.child
.logfile
.write("+ %s\n" % cmd
)
195 self
.command("set confirm off")
196 self
.command("set width 0")
197 self
.command("set height 0")
199 self
.command("set print entry-values no")
202 """Wait for prompt."""
203 self
.child
.expect(r
"\(gdb\)")
205 def command(self
, command
, timeout
=-1):
206 self
.child
.sendline(command
)
207 self
.child
.expect("\n", timeout
=timeout
)
208 self
.child
.expect(r
"\(gdb\)", timeout
=timeout
)
209 return self
.child
.before
.strip()
211 def c(self
, wait
=True):
213 output
= self
.command("c")
214 assert "Continuing" in output
217 self
.child
.sendline("c")
218 self
.child
.expect("Continuing")
221 self
.child
.send("\003")
222 self
.child
.expect(r
"\(gdb\)", timeout
=60)
223 return self
.child
.before
.strip()
225 def x(self
, address
, size
='w'):
226 output
= self
.command("x/%s %s" % (size
, address
))
227 value
= int(output
.split(':')[1].strip(), 0)
231 output
= self
.command("p/x %s" % obj
)
232 m
= re
.search("Cannot access memory at address (0x[0-9a-f]+)", output
)
234 raise CannotAccess(int(m
.group(1), 0))
235 value
= int(output
.split('=')[-1].strip(), 0)
238 def p_string(self
, obj
):
239 output
= self
.command("p %s" % obj
)
240 value
= shlex
.split(output
.split('=')[-1].strip())[1]
244 output
= self
.command("stepi")
248 output
= self
.command("load", timeout
=60)
249 assert "failed" not in output
250 assert "Transfer rate" in output
252 def b(self
, location
):
253 output
= self
.command("b %s" % location
)
254 assert "not defined" not in output
255 assert "Breakpoint" in output
258 def hbreak(self
, location
):
259 output
= self
.command("hbreak %s" % location
)
260 assert "not defined" not in output
261 assert "Hardware assisted breakpoint" in output
264 def run_all_tests(module
, target
, tests
, fail_fast
):
265 good_results
= set(('pass', 'not_applicable'))
271 for name
in dir(module
):
272 definition
= getattr(module
, name
)
273 if type(definition
) == type and hasattr(definition
, 'test') and \
274 (not tests
or any(test
in name
for test
in tests
)):
275 instance
= definition(target
)
276 result
= instance
.run()
277 results
.setdefault(result
, []).append(name
)
279 if result
not in good_results
and fail_fast
:
282 header("ran %d tests in %.0fs" % (count
, time
.time() - start
), dash
=':')
285 for key
, value
in results
.iteritems():
286 print "%d tests returned %s" % (len(value
), key
)
287 if key
not in good_results
:
294 def add_test_run_options(parser
):
295 parser
.add_argument("--fail-fast", "-f", action
="store_true",
296 help="Exit as soon as any test fails.")
297 parser
.add_argument("test", nargs
='*',
298 help="Run only tests that are named here.")
300 def header(title
, dash
='-'):
301 dashes
= dash
* (36 - len(title
))
302 before
= dashes
[:len(dashes
)/2]
303 after
= dashes
[len(dashes
)/2:]
304 print "%s[ %s ]%s" % (before
, title
, after
)
306 class BaseTest(object):
310 def __init__(self
, target
):
313 self
.target_process
= None
317 def early_applicable(self
):
318 """Return a false value if the test has determined it cannot run
319 without ever needing to talk to the target or server."""
320 # pylint: disable=no-self-use
327 compile_args
= getattr(self
, 'compile_args', None)
329 if compile_args
not in BaseTest
.compiled
:
331 # pylint: disable=star-args
332 BaseTest
.compiled
[compile_args
] = \
333 self
.target
.compile(*compile_args
)
334 except Exception: # pylint: disable=broad-except
335 print "exception while compiling in %.2fs" % (
336 time
.time() - self
.start
)
339 traceback
.print_exc(file=sys
.stdout
)
342 self
.binary
= BaseTest
.compiled
.get(compile_args
)
344 def classSetup(self
):
346 self
.target_process
= self
.target
.target()
347 self
.server
= self
.target
.server()
348 self
.logs
.append(self
.server
.logname
)
350 def classTeardown(self
):
352 del self
.target_process
356 If compile_args is set, compile a program and set self.binary.
360 Then call test() and return the result, displaying relevant information
361 if an exception is raised.
364 print "Running", type(self
).__name
__, "...",
367 if not self
.early_applicable():
368 print "not_applicable"
369 return "not_applicable"
371 self
.start
= time
.time()
377 result
= self
.test() # pylint: disable=no-member
378 except Exception as e
: # pylint: disable=broad-except
379 if isinstance(e
, TestFailed
):
383 print "%s in %.2fs" % (result
, time
.time() - self
.start
)
385 if isinstance(e
, TestFailed
):
389 traceback
.print_exc(file=sys
.stdout
)
390 for log
in self
.logs
:
392 print open(log
, "r").read()
401 print "%s in %.2fs" % (result
, time
.time() - self
.start
)
404 class TestFailed(Exception):
405 def __init__(self
, message
):
406 Exception.__init
__(self
)
407 self
.message
= message
409 def assertEqual(a
, b
):
411 raise TestFailed("%r != %r" % (a
, b
))
413 def assertNotEqual(a
, b
):
415 raise TestFailed("%r == %r" % (a
, b
))
419 raise TestFailed("%r not in %r" % (a
, b
))
421 def assertNotIn(a
, b
):
423 raise TestFailed("%r in %r" % (a
, b
))
425 def assertGreater(a
, b
):
427 raise TestFailed("%r not greater than %r" % (a
, b
))
431 raise TestFailed("%r is not True" % a
)
433 def assertRegexpMatches(text
, regexp
):
434 if not re
.search(regexp
, text
):
435 raise TestFailed("can't find %r in %r" % (regexp
, text
))