Merge pull request #117 from riscv/multicore_debug
[riscv-isa-sim.git] / tests / testlib.py
index 274ba6ce576764654cd82f0a88cd5f2a24eec374..d5e8d795c9e5370a515979a4512551d0bace1243 100644 (file)
@@ -13,20 +13,62 @@ def find_file(path):
         fullpath = os.path.join(directory, path)
         if os.path.exists(fullpath):
             return fullpath
-    raise ValueError("Couldn't find %r." % path)
+    return None
 
-def compile(src, dst):
+def compile(*args):
     """Compile a single .c file into a binary."""
+    dst = os.path.splitext(args[0])[0]
     cc = os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gcc")
-    return os.system("%s -g -o %s %s" % (cc, dst, find_file(src)))
+    cmd = [cc, "-g", "-O", "-o", dst]
+    for arg in args:
+        found = find_file(arg)
+        if found:
+            cmd.append(found)
+        else:
+            cmd.append(arg)
+    cmd = " ".join(cmd)
+    result = os.system(cmd)
+    assert result == 0, "%r failed" % cmd
+    return dst
 
-def spike(binary, halted=False):
-    cmd = [find_file("spike")]
-    if halted:
-        cmd.append('-H')
-    cmd += ['pk', binary]
-    logfile = open("spike.log", "w")
-    return subprocess.Popen(cmd, stdout=logfile, stderr=logfile)
+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))
+    port = s.getsockname()[1]
+    s.close()
+    return port
+
+class Spike(object):
+    def __init__(self, binary, halted=False, with_gdb=True, timeout=None):
+        """Launch spike. Return tuple of its process and the port it's running on."""
+        cmd = []
+        if timeout:
+            cmd += ["timeout", str(timeout)]
+
+        cmd += [find_file("spike")]
+        if halted:
+            cmd.append('-H')
+        if with_gdb:
+            self.port = unused_port()
+            cmd += ['--gdb-port', str(self.port)]
+        cmd.append('pk')
+        if binary:
+            cmd.append(binary)
+        logfile = open("spike.log", "w")
+        self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile,
+                stderr=logfile)
+
+    def __del__(self):
+        try:
+            self.process.kill()
+            self.process.wait()
+        except OSError:
+            pass
+
+    def wait(self, *args, **kwargs):
+        return self.process.wait(*args, **kwargs)
 
 class Gdb(object):
     def __init__(self):
@@ -34,13 +76,41 @@ class Gdb(object):
         self.child = pexpect.spawn(path)
         self.child.logfile = file("gdb.log", "w")
         self.wait()
+        self.command("set width 0")
+        self.command("set height 0")
+        # Force consistency.
+        self.command("set print entry-values no")
 
     def wait(self):
         """Wait for prompt."""
         self.child.expect("\(gdb\)")
 
-    def command(self, command):
+    def command(self, command, timeout=-1):
         self.child.sendline(command)
-        self.child.expect("\n")
-        self.child.expect("\(gdb\)")
+        self.child.expect("\n", timeout=timeout)
+        self.child.expect("\(gdb\)", timeout=timeout)
         return self.child.before.strip()
+
+    def c(self, wait=True):
+        if wait:
+            return self.command("c")
+        else:
+            self.child.sendline("c")
+            self.child.expect("Continuing")
+
+    def interrupt(self):
+        self.child.send("\003");
+        self.child.expect("\(gdb\)")
+
+    def x(self, address, size='w'):
+        output = self.command("x/%s %s" % (size, address))
+        value = int(output.split(':')[1].strip(), 0)
+        return value
+
+    def p(self, obj):
+        output = self.command("p %s" % obj)
+        value = int(output.split('=')[-1].strip())
+        return value
+
+    def stepi(self):
+        return self.command("stepi")