Add basic multicore test.
authorTim Newsome <tim@sifive.com>
Fri, 23 Jun 2017 01:37:55 +0000 (18:37 -0700)
committerTim Newsome <tim@sifive.com>
Fri, 23 Jun 2017 01:37:55 +0000 (18:37 -0700)
debug/gdbserver.py
debug/programs/infinite_loop.S
debug/testlib.py

index 4dc00c4dc7eedf1732fd888794fc2afcf5d3463e..2dcd4043c147b338ed2afa5a1420b7c89863c241 100755 (executable)
@@ -10,7 +10,7 @@ import os
 
 import targets
 import testlib
-from testlib import assertEqual, assertNotEqual, assertIn
+from testlib import assertEqual, assertNotEqual, assertIn, assertNotIn
 from testlib import assertGreater, assertRegexpMatches, assertLess
 from testlib import GdbTest
 
@@ -399,6 +399,68 @@ class UserInterrupt(DebugTest):
         self.gdb.p("i=0")
         self.exit()
 
+class MulticoreTest(GdbTest):
+    compile_args = ("programs/infinite_loop.S", )
+
+    def setup(self):
+        self.gdb.load()
+        self.gdb.b("main")
+        self.gdb.b("main_end")
+        self.gdb.command("set non-stop on")
+        self.gdb.c()
+
+    def test(self):
+        threads = self.gdb.threads()
+        if len(threads) < 2:
+            return 'not_applicable'
+        # Run through the entire loop.
+        for t in threads:
+            self.gdb.thread(t)
+            self.gdb.p("$pc=_start")
+        # Run to main
+        for t in threads:
+            self.gdb.thread(t)
+            self.gdb.c()
+        for t in self.gdb.threads():
+            assertIn("main", t.frame)
+        # Run to end
+        for t in threads:
+            self.gdb.thread(t)
+            self.gdb.c()
+        hart_ids = []
+        for t in self.gdb.threads():
+            assertIn("main_end", t.frame)
+            # Check register values.
+            self.gdb.thread(t)
+            hart_id = self.gdb.p("$x1")
+            assertNotIn(hart_id, hart_ids)
+            hart_ids.append(hart_id)
+            for n in range(2, 32):
+                value = self.gdb.p("$x%d" % n)
+                assertEqual(value, hart_ids[-1] + n - 1)
+
+        # Confirmed that we read different register values for different harts.
+        # Write a new value to x1, and run through the add sequence again.
+
+        # This part isn't working right, because gdb doesn't resume Thread 2
+        # when asked. I don't know the root cause for that, but up to this
+        # point the test is still useful.
+
+#        for t in threads:
+#            self.gdb.thread(t)
+#            self.gdb.p("$x1=0x%x" % (int(t.id) + 0x800))
+#            self.gdb.p("$pc=main_post_csrr")
+#        for t in threads:
+#            self.gdb.thread(t)
+#            self.gdb.c()
+#        for t in self.gdb.threads():
+#            assertIn("main_end", t.frame)
+#            # Check register values.
+#            self.gdb.thread(t)
+#            for n in range(1, 32):
+#                value = self.gdb.p("$x%d" % n)
+#                assertEqual(value, int(t.id) + 0x800 + n - 1)
+
 class StepTest(GdbTest):
     compile_args = ("programs/step.S", )
 
index 4b8314372a043a04390f39f2550feb9bb6156b3d..6f15f455cd47a821501b39f1dd9410c9ac6ce78e 100644 (file)
@@ -1,40 +1,43 @@
 #include "encoding.h"
 
         .global main
+        .global main_end
+        .global main_post_csrr
 
         // Load constants into all registers so we can test no register are
         // clobbered after attaching.
 main:
-        csrr    x1, CSR_MISA
-        slli    x2, x1, 1
-        slli    x3, x2, 1
-        slli    x4, x3, 1
-        slli    x5, x4, 1
-        slli    x6, x5, 1
-        slli    x7, x6, 1
-        slli    x8, x7, 1
-        slli    x9, x8, 1
-        slli    x10, x9, 1
-        slli    x11, x10, 1
-        slli    x12, x11, 1
-        slli    x13, x12, 1
-        slli    x14, x13, 1
-        slli    x15, x14, 1
-        slli    x16, x15, 1
-        slli    x17, x16, 1
-        slli    x18, x17, 1
-        slli    x19, x18, 1
-        slli    x20, x19, 1
-        slli    x21, x20, 1
-        slli    x22, x21, 1
-        slli    x23, x22, 1
-        slli    x24, x23, 1
-        slli    x25, x24, 1
-        slli    x26, x25, 1
-        slli    x27, x26, 1
-        slli    x28, x27, 1
-        slli    x29, x28, 1
-        slli    x30, x29, 1
-        slli    x31, x30, 1
-1:
-        j       1b
+        csrr    x1, CSR_MHARTID
+main_post_csrr:
+        addi    x2, x1, 1
+        addi    x3, x2, 1
+        addi    x4, x3, 1
+        addi    x5, x4, 1
+        addi    x6, x5, 1
+        addi    x7, x6, 1
+        addi    x8, x7, 1
+        addi    x9, x8, 1
+        addi    x10, x9, 1
+        addi    x11, x10, 1
+        addi    x12, x11, 1
+        addi    x13, x12, 1
+        addi    x14, x13, 1
+        addi    x15, x14, 1
+        addi    x16, x15, 1
+        addi    x17, x16, 1
+        addi    x18, x17, 1
+        addi    x19, x18, 1
+        addi    x20, x19, 1
+        addi    x21, x20, 1
+        addi    x22, x21, 1
+        addi    x23, x22, 1
+        addi    x24, x23, 1
+        addi    x25, x24, 1
+        addi    x26, x25, 1
+        addi    x27, x26, 1
+        addi    x28, x27, 1
+        addi    x29, x28, 1
+        addi    x30, x29, 1
+        addi    x31, x30, 1
+main_end:
+        j       main
index b8ff5c2edceb3e6d6b1c6982f15d542ae5ae2f51..b59e6e81e9ac1a5cff2c8de45c0868069dbece6b 100644 (file)
@@ -1,3 +1,4 @@
+import collections
 import os.path
 import random
 import re
@@ -294,6 +295,9 @@ class CannotAccess(Exception):
         Exception.__init__(self)
         self.address = address
 
+Thread = collections.namedtuple('Thread', ('id', 'target_id', 'name',
+    'frame'))
+
 class Gdb(object):
     logfile = tempfile.NamedTemporaryFile(prefix="gdb", suffix=".log")
     logname = logfile.name
@@ -320,13 +324,17 @@ class Gdb(object):
         self.child.expect(r"\(gdb\)", timeout=timeout)
         return self.child.before.strip()
 
-    def c(self, wait=True, timeout=-1):
+    def c(self, wait=True, timeout=-1, async=False):
+        if async:
+            async = "&"
+        else:
+            async = ""
         if wait:
-            output = self.command("c", timeout=timeout)
+            output = self.command("c%s" % async, timeout=timeout)
             assert "Continuing" in output
             return output
         else:
-            self.child.sendline("c")
+            self.child.sendline("c%s" % async)
             self.child.expect("Continuing")
 
     def interrupt(self):
@@ -380,6 +388,22 @@ class Gdb(object):
         assert "Hardware assisted breakpoint" in output
         return output
 
+    def threads(self):
+        output = self.command("info threads")
+        threads = []
+        for line in output.splitlines():
+            m = re.match(
+                    r"[\s\*]*(\d+)\s*Thread (\d+)\s*\(Name: ([^\)]+)\s*(.*)",
+                    line)
+            if m:
+                threads.append(Thread(*m.groups()))
+        if not threads:
+            threads.append(Thread('1', '1', 'Default', '???'))
+        return threads
+
+    def thread(self, thread):
+        return self.command("thread %s" % thread.id)
+
 def run_all_tests(module, target, parsed):
     if not os.path.exists(parsed.logs):
         os.makedirs(parsed.logs)
@@ -594,11 +618,8 @@ class GdbTest(BaseTest):
                     "target extended-remote localhost:%d" % self.server.port)
             # Select a random thread.
             # TODO: Allow a command line option to force a specific thread.
-            output = self.gdb.command("info threads")
-            threads = re.findall(r"Thread (\d+)", output)
-            if threads:
-                thread = random.choice(threads)
-                self.gdb.command("thread %s" % thread)
+            thread = random.choice(self.gdb.threads())
+            self.gdb.thread(thread)
 
         # FIXME: OpenOCD doesn't handle PRIV now
         #self.gdb.p("$priv=3")