X-Git-Url: https://git.libre-soc.org/?p=riscv-tests.git;a=blobdiff_plain;f=debug%2Ftestlib.py;h=29fa1703efdd81f339e726a649e700bac4d77bf1;hp=53e670ec51c006fa96cbb0ea2f201e5f77fb7f7c;hb=c5e29cf553799b0434e2635e3f42314af337a5b2;hpb=440f54715a4a4335a371c61bc79079ead8c3c6fe diff --git a/debug/testlib.py b/debug/testlib.py index 53e670e..29fa170 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -1,26 +1,24 @@ import os.path -import pexpect +import re import shlex import subprocess -import tempfile -import testlib -import unittest +import time + +import pexpect # Note that gdb comes with its own testsuite. I was unable to figure out how to # run that testsuite against the spike simulator. def find_file(path): - for directory in (os.getcwd(), os.path.dirname(testlib.__file__)): + for directory in (os.getcwd(), os.path.dirname(__file__)): fullpath = os.path.join(directory, path) if os.path.exists(fullpath): return fullpath return None -def compile(args, xlen=32): - """Compile a single .c file into a binary.""" - dst = os.path.splitext(args[0])[0] +def compile(args, xlen=32): # pylint: disable=redefined-builtin cc = os.path.expandvars("$RISCV/bin/riscv%d-unknown-elf-gcc" % xlen) - cmd = [cc, "-g", "-o", dst] + cmd = [cc, "-g"] for arg in args: found = find_file(arg) if found: @@ -30,26 +28,28 @@ def compile(args, xlen=32): cmd = " ".join(cmd) result = os.system(cmd) assert result == 0, "%r failed" % cmd - return dst def unused_port(): # http://stackoverflow.com/questions/2838244/get-open-tcp-port-in-python/2838309#2838309 import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind(("",0)) + s.bind(("", 0)) port = s.getsockname()[1] s.close() return port class Spike(object): - def __init__(self, cmd, binary=None, halted=False, with_gdb=True, timeout=None, - xlen=64): - """Launch spike. Return tuple of its process and the port it's running on.""" + logname = "spike.log" + + def __init__(self, cmd, binary=None, halted=False, with_gdb=True, + timeout=None, xlen=64): + """Launch spike. Return tuple of its process and the port it's running + on.""" if cmd: cmd = shlex.split(cmd) else: cmd = ["spike"] - if (xlen == 32): + if xlen == 32: cmd += ["--isa", "RV32"] if timeout: @@ -60,13 +60,15 @@ class Spike(object): if with_gdb: self.port = unused_port() cmd += ['--gdb-port', str(self.port)] + cmd.append("-m32") cmd.append('pk') if binary: cmd.append(binary) - logfile = open("spike.log", "w") + logfile = open(self.logname, "w") logfile.write("+ %s\n" % " ".join(cmd)) - self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile, - stderr=logfile) + logfile.flush() + self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=logfile, stderr=logfile) def __del__(self): try: @@ -78,8 +80,47 @@ class Spike(object): def wait(self, *args, **kwargs): return self.process.wait(*args, **kwargs) +class VcsSim(object): + def __init__(self, simv=None, debug=False): + if simv: + cmd = shlex.split(simv) + else: + cmd = ["simv"] + cmd += ["+jtag_vpi_enable"] + if debug: + cmd[0] = cmd[0] + "-debug" + cmd += ["+vcdplusfile=output/gdbserver.vpd"] + logfile = open("simv.log", "w") + logfile.write("+ %s\n" % " ".join(cmd)) + logfile.flush() + listenfile = open("simv.log", "r") + listenfile.seek(0, 2) + self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=logfile, stderr=logfile) + done = False + while not done: + line = listenfile.readline() + if not line: + time.sleep(1) + if "Listening on port 5555" in line: + done = True + + def __del__(self): + try: + self.process.kill() + self.process.wait() + except OSError: + pass + class Openocd(object): - def __init__(self, cmd=None, config=None, debug=True): + logname = "openocd.log" + + def __init__(self, cmd=None, config=None, debug=False, otherProcess=None): + + # keep handles to other processes -- don't let them be + # garbage collected yet. + + self.otherProcess = otherProcess if cmd: cmd = shlex.split(cmd) else: @@ -88,11 +129,34 @@ class Openocd(object): cmd += ["-f", find_file(config)] if debug: cmd.append("-d") - logfile = open("openocd.log", "w") - self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile, - stderr=logfile) - # TODO: Pick a random port - self.port = 3333 + + # Assign port + self.port = unused_port() + print "Using port %d for gdb server" % self.port + # This command needs to come before any config scripts on the command + # line, since they are executed in order. + cmd[1:1] = ["--command", "gdb_port %d" % self.port] + + logfile = open(Openocd.logname, "w") + logfile.write("+ %s\n" % " ".join(cmd)) + logfile.flush() + self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=logfile, stderr=logfile) + + # Wait for OpenOCD to have made it through riscv_examine(). When using + # OpenOCD to communicate with a simulator this may take a long time, + # and gdb will time out when trying to connect if we attempt too early. + start = time.time() + messaged = False + while True: + log = open(Openocd.logname).read() + if "Examined RISCV core" in log: + break + if not self.process.poll() is None: + raise Exception("OpenOCD exited before completing riscv_examine()") + if not messaged and time.time() - start > 1: + messaged = True + print "Waiting for OpenOCD to examine RISCV core..." def __del__(self): try: @@ -101,11 +165,17 @@ class Openocd(object): except OSError: pass +class CannotAccess(Exception): + def __init__(self, address): + Exception.__init__(self) + self.address = address + class Gdb(object): - def __init__(self): - path = os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gdb") - self.child = pexpect.spawn(path) - self.child.logfile = file("gdb.log", "w") + def __init__(self, + cmd=os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gdb")): + self.child = pexpect.spawn(cmd) + self.child.logfile = open("gdb.log", "w") + self.child.logfile.write("+ %s\n" % cmd) self.wait() self.command("set confirm off") self.command("set width 0") @@ -115,12 +185,12 @@ class Gdb(object): def wait(self): """Wait for prompt.""" - self.child.expect("\(gdb\)") + self.child.expect(r"\(gdb\)") def command(self, command, timeout=-1): self.child.sendline(command) self.child.expect("\n", timeout=timeout) - self.child.expect("\(gdb\)", timeout=timeout) + self.child.expect(r"\(gdb\)", timeout=timeout) return self.child.before.strip() def c(self, wait=True): @@ -133,8 +203,8 @@ class Gdb(object): self.child.expect("Continuing") def interrupt(self): - self.child.send("\003"); - self.child.expect("\(gdb\)") + self.child.send("\003") + self.child.expect(r"\(gdb\)", timeout=60) return self.child.before.strip() def x(self, address, size='w'): @@ -144,16 +214,23 @@ class Gdb(object): def p(self, obj): output = self.command("p/x %s" % obj) + m = re.search("Cannot access memory at address (0x[0-9a-f]+)", output) + if m: + raise CannotAccess(int(m.group(1), 0)) value = int(output.split('=')[-1].strip(), 0) return value + def p_string(self, obj): + output = self.command("p %s" % obj) + value = shlex.split(output.split('=')[-1].strip())[1] + return value + def stepi(self): output = self.command("stepi") - assert "Cannot" not in output return output def load(self): - output = self.command("load") + output = self.command("load", timeout=60) assert "failed" not in output assert "Transfer rate" in output