Test resuming from a trigger.
[riscv-tests.git] / debug / testlib.py
index ce8aecaafbf3369e718e93b100f4276747207224..3aaa542a5c76dce60bf92ca485119e2045b3dbe0 100644 (file)
@@ -56,10 +56,12 @@ def compile(args, xlen=32): # pylint: disable=redefined-builtin
         raise Exception("Compile failed!")
 
 class Spike(object):
-    def __init__(self, target, halted=False, timeout=None, with_jtag_gdb=True):
+    def __init__(self, target, halted=False, timeout=None, with_jtag_gdb=True,
+            isa=None):
         """Launch spike. Return tuple of its process and the port it's running
         on."""
         self.process = None
+        self.isa = isa
 
         if target.harts:
             harts = target.harts
@@ -109,10 +111,12 @@ class Spike(object):
         assert len(set(t.xlen for t in harts)) == 1, \
                 "All spike harts must have the same XLEN"
 
-        if harts[0].xlen == 32:
-            cmd += ["--isa", "RV32G"]
+        if self.isa:
+            isa = self.isa
         else:
-            cmd += ["--isa", "RV64G"]
+            isa = "RV%dG" % harts[0].xlen
+
+        cmd += ["--isa", isa]
 
         assert len(set(t.ram for t in harts)) == 1, \
                 "All spike harts must have the same RAM layout"
@@ -325,6 +329,12 @@ class CannotAccess(Exception):
         Exception.__init__(self)
         self.address = address
 
+class CouldNotFetch(Exception):
+    def __init__(self, regname, explanation):
+        Exception.__init__(self)
+        self.regname = regname
+        self.explanation = explanation
+
 Thread = collections.namedtuple('Thread', ('id', 'description', 'target_id',
     'name', 'frame'))
 
@@ -386,12 +396,18 @@ class Gdb(object):
                         hartid = max(self.harts) + 1
                     else:
                         hartid = 0
-                self.harts[hartid] = (child, t)
+                # solo: True iff this is the only thread on this child
+                self.harts[hartid] = {'child': child,
+                        'thread': t,
+                        'solo': len(threads) == 1}
 
     def __del__(self):
         for child in self.children:
             del child
 
+    def one_hart_per_gdb(self):
+        return all(h['solo'] for h in self.harts.itervalues())
+
     def lognames(self):
         return [logfile.name for logfile in self.logfiles]
 
@@ -399,10 +415,11 @@ class Gdb(object):
         self.active_child = child
 
     def select_hart(self, hart):
-        child, thread = self.harts[hart.id]
-        self.select_child(child)
-        output = self.command("thread %s" % thread.id)
-        assert "Unknown" not in output
+        h = self.harts[hart.id]
+        self.select_child(h['child'])
+        if not h['solo']:
+            output = self.command("thread %s" % h['thread'].id, timeout=10)
+            assert "Unknown" not in output
 
     def push_state(self):
         self.stack.append({
@@ -503,6 +520,9 @@ class Gdb(object):
         m = re.search("Cannot access memory at address (0x[0-9a-f]+)", output)
         if m:
             raise CannotAccess(int(m.group(1), 0))
+        m = re.search(r"Could not fetch register \"(\w+)\"; (.*)$", output)
+        if m:
+            raise CouldNotFetch(m.group(1), m.group(2))
         rhs = output.split('=')[-1]
         return self.parse_string(rhs)
 
@@ -815,6 +835,7 @@ class GdbTest(BaseTest):
         if not self.gdb:
             return
         self.gdb.interrupt()
+        self.gdb.command("disassemble")
         self.gdb.command("info registers all", timeout=10)
 
     def classTeardown(self):