Make the debug tests aware of multicore.
authorTim Newsome <tim@sifive.com>
Mon, 7 Aug 2017 19:51:42 +0000 (12:51 -0700)
committerTim Newsome <tim@sifive.com>
Mon, 28 Aug 2017 19:16:39 +0000 (12:16 -0700)
Targets now contain an array of harts. When running a regular test, one
hart is selected to run the test on while the remaining harts are parked
in a safe infinite loop.

There's currently only one test that tests multicore behavior, but there
could be more.

The infrastructure should be able to support heterogeneous multicore,
but I don't have a target like that to test with.

18 files changed:
debug/Makefile
debug/gdbserver.py
debug/programs/entry.S
debug/programs/start.S [deleted file]
debug/targets.py
debug/targets/RISC-V/spike.cfg [new file with mode: 0644]
debug/targets/RISC-V/spike32-2.py [new file with mode: 0644]
debug/targets/RISC-V/spike32.cfg [deleted file]
debug/targets/RISC-V/spike32.py
debug/targets/RISC-V/spike64-2.py [new file with mode: 0644]
debug/targets/RISC-V/spike64.cfg [deleted file]
debug/targets/RISC-V/spike64.py
debug/targets/SiFive/Freedom/E300.py
debug/targets/SiFive/Freedom/E300Sim.py
debug/targets/SiFive/Freedom/U500.py
debug/targets/SiFive/Freedom/U500Sim.py
debug/targets/SiFive/HiFive1.py
debug/testlib.py

index 2d8d36759d0aac813935b88d88771d9caca8ad32..33988dd02615fae0a40517bedc85f80f7b4a29cf 100644 (file)
@@ -4,9 +4,9 @@ XLEN ?= 64
 src_dir ?= .
 GDBSERVER_PY = $(src_dir)/gdbserver.py
 
-default: spike$(XLEN)
+default: spike$(XLEN)-2
 
-all:   pylint spike32 spike64
+all:   pylint spike32 spike64 spike32-2 spike64-2
 
 pylint:
        pylint --rcfile=pylint.rc `git ls-files '*.py'`
index cbb1299ad9ecafc009df6c486a8e656f49fde011..092f0181907f31db82b9806c2e574bbfdef7c381 100755 (executable)
@@ -12,7 +12,7 @@ import targets
 import testlib
 from testlib import assertEqual, assertNotEqual, assertIn, assertNotIn
 from testlib import assertGreater, assertRegexpMatches, assertLess
-from testlib import GdbTest
+from testlib import GdbTest, GdbSingleHartTest
 
 MSTATUS_UIE = 0x00000001
 MSTATUS_SIE = 0x00000002
@@ -66,8 +66,8 @@ def readable_binary_string(s):
 
 class SimpleRegisterTest(GdbTest):
     def check_reg(self, name):
-        a = random.randrange(1<<self.target.xlen)
-        b = random.randrange(1<<self.target.xlen)
+        a = random.randrange(1<<self.hart.xlen)
+        b = random.randrange(1<<self.hart.xlen)
         self.gdb.p("$%s=0x%x" % (name, a))
         self.gdb.stepi()
         assertEqual(self.gdb.p("$%s" % name), a)
@@ -77,12 +77,12 @@ class SimpleRegisterTest(GdbTest):
 
     def setup(self):
         # 0x13 is nop
-        self.gdb.command("p *((int*) 0x%x)=0x13" % self.target.ram)
-        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 4))
-        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 8))
-        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 12))
-        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 16))
-        self.gdb.p("$pc=0x%x" % self.target.ram)
+        self.gdb.command("p *((int*) 0x%x)=0x13" % self.hart.ram)
+        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 4))
+        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 8))
+        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 12))
+        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 16))
+        self.gdb.p("$pc=0x%x" % self.hart.ram)
 
 class SimpleS0Test(SimpleRegisterTest):
     def test(self):
@@ -114,7 +114,8 @@ class SimpleF18Test(SimpleRegisterTest):
         assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - b), .001)
 
     def early_applicable(self):
-        return self.target.extensionSupported('F')
+        print repr(self.hart)
+        return self.hart.extensionSupported('F')
 
     def test(self):
         self.check_reg("f18")
@@ -157,7 +158,7 @@ class MemTest64(SimpleMemoryTest):
 #            assert False, "Read should have failed."
 #        except testlib.CannotAccess as e:
 #            assertEqual(e.address, 0xdeadbeef)
-#        self.gdb.p("*((int*)0x%x)" % self.target.ram)
+#        self.gdb.p("*((int*)0x%x)" % self.hart.ram)
 #
 #class MemTestWriteInvalid(SimpleMemoryTest):
 #    def test(self):
@@ -168,7 +169,7 @@ class MemTest64(SimpleMemoryTest):
 #            assert False, "Write should have failed."
 #        except testlib.CannotAccess as e:
 #            assertEqual(e.address, 0xdeadbeef)
-#        self.gdb.p("*((int*)0x%x)=6874742" % self.target.ram)
+#        self.gdb.p("*((int*)0x%x)=6874742" % self.hart.ram)
 
 class MemTestBlock(GdbTest):
     def test(self):
@@ -183,9 +184,9 @@ class MemTestBlock(GdbTest):
             a.write(ihex_line(i * line_length, 0, line_data))
         a.flush()
 
-        self.gdb.command("restore %s 0x%x" % (a.name, self.target.ram))
+        self.gdb.command("restore %s 0x%x" % (a.name, self.hart.ram))
         for offset in range(0, length, 19*4) + [length-4]:
-            value = self.gdb.p("*((int*)0x%x)" % (self.target.ram + offset))
+            value = self.gdb.p("*((int*)0x%x)" % (self.hart.ram + offset))
             written = ord(data[offset]) | \
                     (ord(data[offset+1]) << 8) | \
                     (ord(data[offset+2]) << 16) | \
@@ -194,7 +195,7 @@ class MemTestBlock(GdbTest):
 
         b = tempfile.NamedTemporaryFile(suffix=".ihex")
         self.gdb.command("dump ihex memory %s 0x%x 0x%x" % (b.name,
-            self.target.ram, self.target.ram + length))
+            self.hart.ram, self.hart.ram + length))
         for line in b:
             record_type, address, line_data = ihex_parse(line)
             if record_type == 0:
@@ -213,7 +214,7 @@ class InstantHaltTest(GdbTest):
             self.gdb.thread(t)
             pcs.append(self.gdb.p("$pc"))
         for pc in pcs:
-            assertEqual(self.target.reset_vector, pc)
+            assertEqual(self.hart.reset_vector, pc)
         # mcycle and minstret have no defined reset value.
         mstatus = self.gdb.p("$mstatus")
         assertEqual(mstatus & (MSTATUS_MIE | MSTATUS_MPRV |
@@ -225,16 +226,16 @@ class InstantChangePc(GdbTest):
         # 0x13 is nop
         self.gdb.command("monitor reset halt")
         self.gdb.command("flushregs")
-        self.gdb.command("p *((int*) 0x%x)=0x13" % self.target.ram)
-        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 4))
-        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 8))
-        self.gdb.p("$pc=0x%x" % self.target.ram)
+        self.gdb.command("p *((int*) 0x%x)=0x13" % self.hart.ram)
+        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 4))
+        self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 8))
+        self.gdb.p("$pc=0x%x" % self.hart.ram)
         self.gdb.stepi()
-        assertEqual((self.target.ram + 4), self.gdb.p("$pc"))
+        assertEqual((self.hart.ram + 4), self.gdb.p("$pc"))
         self.gdb.stepi()
-        assertEqual((self.target.ram + 8), self.gdb.p("$pc"))
+        assertEqual((self.hart.ram + 8), self.gdb.p("$pc"))
 
-class DebugTest(GdbTest):
+class DebugTest(GdbSingleHartTest):
     # Include malloc so that gdb can make function calls. I suspect this malloc
     # will silently blow through the memory set aside for it, so be careful.
     compile_args = ("programs/debug.c", "programs/checksum.c",
@@ -325,10 +326,10 @@ class DebugBreakpoint(DebugTest):
 
 class Hwbp1(DebugTest):
     def test(self):
-        if self.target.instruction_hardware_breakpoint_count < 1:
+        if self.hart.instruction_hardware_breakpoint_count < 1:
             return 'not_applicable'
 
-        if not self.target.honors_tdata1_hmode:
+        if not self.hart.honors_tdata1_hmode:
             # Run to main before setting the breakpoint, because startup code
             # will otherwise clear the trigger that we set.
             self.gdb.b("main")
@@ -345,7 +346,7 @@ class Hwbp1(DebugTest):
 
 class Hwbp2(DebugTest):
     def test(self):
-        if self.target.instruction_hardware_breakpoint_count < 2:
+        if self.hart.instruction_hardware_breakpoint_count < 2:
             return 'not_applicable'
 
         self.gdb.hbreak("main")
@@ -418,16 +419,15 @@ class UserInterrupt(DebugTest):
 class MulticoreTest(GdbTest):
     compile_args = ("programs/infinite_loop.S", )
 
+    def early_applicable(self):
+        return len(self.target.harts) > 1
+
     def setup(self):
         self.gdb.load()
 
     def test(self):
-        threads = self.gdb.threads()
-        if len(threads) < 2:
-            return 'not_applicable'
-
-        for t in threads:
-            self.gdb.thread(t)
+        for hart in self.target.harts:
+            self.gdb.select_hart(hart)
             self.gdb.p("$pc=_start")
 
         # Run to main
@@ -456,18 +456,19 @@ class MulticoreTest(GdbTest):
         # Confirmed that we read different register values for different harts.
         # Write a new value to x1, and run through the add sequence again.
 
-        for t in threads:
-            self.gdb.thread(t)
-            self.gdb.p("$x1=0x%x" % (int(t.id) * 0x800))
+        for hart in self.target.harts:
+            self.gdb.select_hart(hart)
+            self.gdb.p("$x1=0x%x" % (hart.index * 0x800))
             self.gdb.p("$pc=main_post_csrr")
         self.gdb.c()
         for t in self.gdb.threads():
             assertIn("main_end", t.frame)
+        for hart in self.target.harts:
             # Check register values.
-            self.gdb.thread(t)
+            self.gdb.select_hart(hart)
             for n in range(1, 32):
                 value = self.gdb.p("$x%d" % n)
-                assertEqual(value, int(t.id) * 0x800 + n - 1)
+                assertEqual(value, hart.index * 0x800 + n - 1)
 
 class StepTest(GdbTest):
     compile_args = ("programs/step.S", )
@@ -479,7 +480,7 @@ class StepTest(GdbTest):
 
     def test(self):
         main_address = self.gdb.p("$pc")
-        if self.target.extensionSupported("c"):
+        if self.hart.extensionSupported("c"):
             sequence = (4, 8, 0xc, 0xe, 0x14, 0x18, 0x22, 0x1c, 0x24, 0x24)
         else:
             sequence = (4, 8, 0xc, 0x10, 0x18, 0x1c, 0x28, 0x20, 0x2c, 0x2c)
@@ -558,16 +559,16 @@ class TriggerStoreAddressInstant(TriggerTest):
 
 class TriggerDmode(TriggerTest):
     def early_applicable(self):
-        return self.target.honors_tdata1_hmode
+        return self.hart.honors_tdata1_hmode
 
     def check_triggers(self, tdata1_lsbs, tdata2):
-        dmode = 1 << (self.target.xlen-5)
+        dmode = 1 << (self.hart.xlen-5)
 
         triggers = []
 
-        if self.target.xlen == 32:
+        if self.hart.xlen == 32:
             xlen_type = 'int'
-        elif self.target.xlen == 64:
+        elif self.hart.xlen == 64:
             xlen_type = 'long long'
         else:
             raise NotImplementedError
@@ -627,7 +628,7 @@ class WriteGprs(RegsTest):
         self.gdb.command("info registers")
         for n in range(len(regs)):
             assertEqual(self.gdb.x("data+%d" % (8*n), 'g'),
-                    ((0xdeadbeef<<n)+17) & ((1<<self.target.xlen)-1))
+                    ((0xdeadbeef<<n)+17) & ((1<<self.hart.xlen)-1))
 
 class WriteCsrs(RegsTest):
     def test(self):
@@ -651,7 +652,7 @@ class WriteCsrs(RegsTest):
 class DownloadTest(GdbTest):
     def setup(self):
         # pylint: disable=attribute-defined-outside-init
-        length = min(2**10, self.target.ram_size - 2048)
+        length = min(2**10, self.hart.ram_size - 2048)
         self.download_c = tempfile.NamedTemporaryFile(prefix="download_",
                 suffix=".c", delete=False)
         self.download_c.write("#include <stdint.h>\n")
@@ -677,7 +678,7 @@ class DownloadTest(GdbTest):
         if self.crc < 0:
             self.crc += 2**32
 
-        self.binary = self.target.compile(self.download_c.name,
+        self.binary = self.hart.compile(self.download_c.name,
                 "programs/checksum.c")
         self.gdb.command("file %s" % self.binary)
 
@@ -708,7 +709,7 @@ class DownloadTest(GdbTest):
 #        # pylint: disable=attribute-defined-outside-init
 #        self.gdb.load()
 #
-#        misa = self.target.misa
+#        misa = self.hart.misa
 #        self.supported = set()
 #        if misa & (1<<20):
 #            self.supported.add(0)
index ff8ae3092a517ad20ce2860b7b56ac2d1cd05b5a..c3be61108e4a129b0ab2b087754d78d9d0ed1972 100755 (executable)
@@ -157,9 +157,16 @@ trap_entry:
   addi sp, sp, 32*REGBYTES
   mret
 
+loop_forever:
+  j loop_forever
+
   // Fill the stack with data so we can see if it was overrun.
   .align 4
 stack_bottom:
   .fill STACK_SIZE/4, 4, 0x22446688
 stack_top:
+  // Prevent stack_top from being identical to next symbol, which may cause gdb
+  // to report we're halted at stack_top which happens to be the same address
+  // as main.
+  .word 0
 #endif
diff --git a/debug/programs/start.S b/debug/programs/start.S
deleted file mode 100644 (file)
index 76c37bb..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-        .global         _start
-
-_start:
-        la      sp, stack_end
-        jal     main
-done:
-        j       done
-
-        .data
-stack:
-        .fill   4096, 1, 0
-stack_end:
index 7183a38fe8011fc68f77d7e63be04abcbc4c7b84..8efa2559d8f72c2539100b2eddfa444e6cdb414f 100644 (file)
@@ -5,88 +5,44 @@ import tempfile
 
 import testlib
 
-class Target(object):
-    # pylint: disable=too-many-instance-attributes
-
-    # Name of the target. Defaults to the name of the class.
-    name = None
-
-    # XLEN of the target. May be overridden with --32 or --64 command line
+class Hart(object):
+    # XLEN of the hart. May be overridden with --32 or --64 command line
     # options.
     xlen = 0
 
-    # GDB remotetimeout setting.
-    timeout_sec = 2
-
-    # Path to OpenOCD configuration file relative to the .py file where the
-    # target is defined. Defaults to <name>.cfg.
-    openocd_config_path = None
-
-    # Timeout waiting for the server to start up. This is different than the
-    # GDB timeout, which is how long GDB waits for commands to execute.
-    # The server_timeout is how long this script waits for the Server to be
-    # ready for GDB connections.
-    server_timeout_sec = 60
-
-    # Path to linker script relative to the .py file where the target is
-    # defined. Defaults to <name>.lds.
-    link_script_path = None
-
     # Will be autodetected (by running ExamineTarget) if left unset. Set to
     # save a little time.
     misa = None
 
-    # List of commands that should be executed in gdb after connecting but
-    # before starting the test.
-    gdb_setup = []
+    # Path to linker script relative to the .py file where the target is
+    # defined. Defaults to <name>.lds.
+    link_script_path = None
 
-    # Implements dmode in tdata1 as described in the spec. Targets that need
+    # Implements dmode in tdata1 as described in the spec. Harts that need
     # this value set to False are not compliant with the spec (but still usable
     # as long as running code doesn't try to mess with triggers set by an
     # external debugger).
     honors_tdata1_hmode = True
 
-    # Internal variables:
-    directory = None
-    temporary_files = []
-    temporary_binary = None
+    # Address where a r/w/x block of RAM starts, together with its size.
+    ram = None
+    ram_size = None
 
-    def __init__(self, path, parsed):
-        # Path to module.
-        self.path = path
-        self.directory = os.path.dirname(path)
-        self.server_cmd = parsed.server_cmd
-        self.sim_cmd = parsed.sim_cmd
-        self.isolate = parsed.isolate
-        if not self.name:
-            self.name = type(self).__name__
-        # Default OpenOCD config file to <name>.cfg
-        if not self.openocd_config_path:
-            self.openocd_config_path = "%s.cfg" % self.name
-        self.openocd_config_path = os.path.join(self.directory,
-                self.openocd_config_path)
-        # Default link script to <name>.lds
-        if not self.link_script_path:
-            self.link_script_path = "%s.lds" % self.name
-        self.link_script_path = os.path.join(self.directory,
-                self.link_script_path)
+    # Number of instruction triggers the hart supports.
+    instruction_hardware_breakpoint_count = 0
 
-    def create(self):
-        """Create the target out of thin air, eg. start a simulator."""
-        pass
+    # Defaults to target-<index>
+    name = None
 
-    def server(self):
-        """Start the debug server that gdb connects to, eg. OpenOCD."""
-        return testlib.Openocd(server_cmd=self.server_cmd,
-                               config=self.openocd_config_path,
-                               timeout=self.server_timeout_sec)
+    def __init__(self):
+        self.temporary_binary = None
 
     def compile(self, *sources):
         binary_name = "%s_%s-%d" % (
                 self.name,
                 os.path.basename(os.path.splitext(sources[0])[0]),
                 self.xlen)
-        if self.isolate:
+        if Target.isolate:
             self.temporary_binary = tempfile.NamedTemporaryFile(
                     prefix=binary_name + "_")
             binary_name = self.temporary_binary.name
@@ -114,6 +70,69 @@ class Target(object):
         else:
             return False
 
+class Target(object):
+    # pylint: disable=too-many-instance-attributes
+
+    # List of Hart object instances, one for each hart in the target.
+    harts = []
+
+    # Name of the target. Defaults to the name of the class.
+    name = None
+
+    # GDB remotetimeout setting.
+    timeout_sec = 2
+
+    # Timeout waiting for the server to start up. This is different than the
+    # GDB timeout, which is how long GDB waits for commands to execute.
+    # The server_timeout is how long this script waits for the Server to be
+    # ready for GDB connections.
+    server_timeout_sec = 60
+
+    # Path to OpenOCD configuration file relative to the .py file where the
+    # target is defined. Defaults to <name>.cfg.
+    openocd_config_path = None
+
+    # List of commands that should be executed in gdb after connecting but
+    # before starting the test.
+    gdb_setup = []
+
+    # Internal variables:
+    directory = None
+    temporary_files = []
+
+    def __init__(self, path, parsed):
+        # Path to module.
+        self.path = path
+        self.directory = os.path.dirname(path)
+        self.server_cmd = parsed.server_cmd
+        self.sim_cmd = parsed.sim_cmd
+        Target.isolate = parsed.isolate
+        if not self.name:
+            self.name = type(self).__name__
+        # Default OpenOCD config file to <name>.cfg
+        if not self.openocd_config_path:
+            self.openocd_config_path = "%s.cfg" % self.name
+        self.openocd_config_path = os.path.join(self.directory,
+                self.openocd_config_path)
+        for i, hart in enumerate(self.harts):
+            hart.index = i
+            if not hart.name:
+                hart.name = "%s-%d" % (self.name, i)
+            # Default link script to <name>.lds
+            if not hart.link_script_path:
+                hart.link_script_path = "%s.lds" % self.name
+            hart.link_script_path = os.path.join(self.directory,
+                    hart.link_script_path)
+
+    def create(self):
+        """Create the target out of thin air, eg. start a simulator."""
+        pass
+
+    def server(self):
+        """Start the debug server that gdb connects to, eg. OpenOCD."""
+        return testlib.Openocd(server_cmd=self.server_cmd,
+                config=self.openocd_config_path)
+
 def add_target_options(parser):
     parser.add_argument("target", help=".py file that contains definition for "
             "the target to test with.")
@@ -149,4 +168,7 @@ def target(parsed):
     assert len(found) == 1, "%s does not define exactly one subclass of " \
             "targets.Target" % parsed.target
 
-    return found[0](parsed.target, parsed)
+    t = found[0](parsed.target, parsed)
+    assert t.harts, "%s doesn't have any harts defined!" % t.name
+
+    return t
diff --git a/debug/targets/RISC-V/spike.cfg b/debug/targets/RISC-V/spike.cfg
new file mode 100644 (file)
index 0000000..9b1841c
--- /dev/null
@@ -0,0 +1,16 @@
+adapter_khz     10000
+
+interface remote_bitbang
+remote_bitbang_host $::env(REMOTE_BITBANG_HOST)
+remote_bitbang_port $::env(REMOTE_BITBANG_PORT)
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv
+
+gdb_report_data_abort enable
+
+init
+reset halt
diff --git a/debug/targets/RISC-V/spike32-2.py b/debug/targets/RISC-V/spike32-2.py
new file mode 100644 (file)
index 0000000..3f87d26
--- /dev/null
@@ -0,0 +1,11 @@
+import targets
+import testlib
+
+import spike32
+
+class spike32_2(targets.Target):
+    harts = [spike32.spike32_hart(), spike32.spike32_hart()]
+    openocd_config_path = "spike.cfg"
+
+    def create(self):
+        return testlib.Spike(self)
diff --git a/debug/targets/RISC-V/spike32.cfg b/debug/targets/RISC-V/spike32.cfg
deleted file mode 100644 (file)
index 2742335..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-adapter_khz     10000
-
-interface remote_bitbang
-remote_bitbang_host $::env(REMOTE_BITBANG_HOST)
-remote_bitbang_port $::env(REMOTE_BITBANG_PORT)
-
-set _CHIPNAME riscv
-jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
-
-set _TARGETNAME $_CHIPNAME.cpu
-#target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv
-target create $_TARGETNAME riscv -chain-position $_TARGETNAME
-
-gdb_report_data_abort enable
-
-init
-reset halt
-
-echo "Ready for Remote Connections"
index 3bf8b4783c4578bb5f671b9036c5a08de37c7ab4..e80f60a07a51e6ae38ee0b2afad51f201e1f0146 100644 (file)
@@ -1,12 +1,17 @@
 import targets
 import testlib
 
-class spike32(targets.Target):
+class spike32_hart(targets.Hart):
     xlen = 32
     ram = 0x10000000
     ram_size = 0x10000000
     instruction_hardware_breakpoint_count = 4
     reset_vector = 0x1000
+    link_script_path = "spike64.lds"
+
+class spike32(targets.Target):
+    harts = [spike32_hart()]
+    openocd_config_path = "spike.cfg"
 
     def create(self):
         return testlib.Spike(self)
diff --git a/debug/targets/RISC-V/spike64-2.py b/debug/targets/RISC-V/spike64-2.py
new file mode 100644 (file)
index 0000000..e1df6d2
--- /dev/null
@@ -0,0 +1,11 @@
+import targets
+import testlib
+
+import spike64
+
+class spike64_2(targets.Target):
+    harts = [spike64.spike64_hart(), spike64.spike64_hart()]
+    openocd_config_path = "spike.cfg"
+
+    def create(self):
+        return testlib.Spike(self)
diff --git a/debug/targets/RISC-V/spike64.cfg b/debug/targets/RISC-V/spike64.cfg
deleted file mode 100644 (file)
index 2742335..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-adapter_khz     10000
-
-interface remote_bitbang
-remote_bitbang_host $::env(REMOTE_BITBANG_HOST)
-remote_bitbang_port $::env(REMOTE_BITBANG_PORT)
-
-set _CHIPNAME riscv
-jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
-
-set _TARGETNAME $_CHIPNAME.cpu
-#target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv
-target create $_TARGETNAME riscv -chain-position $_TARGETNAME
-
-gdb_report_data_abort enable
-
-init
-reset halt
-
-echo "Ready for Remote Connections"
index c70585782622610b4513cbaedbff5fb1fc49af1d..84586e33f46d682d18ab63db92ec4b5c83562da5 100644 (file)
@@ -1,12 +1,17 @@
 import targets
 import testlib
 
-class spike64(targets.Target):
+class spike64_hart(targets.Hart):
     xlen = 64
     ram = 0x1212340000
     ram_size = 0x10000000
     instruction_hardware_breakpoint_count = 4
     reset_vector = 0x1000
+    link_script_path = "spike64.lds"
+
+class spike64(targets.Target):
+    harts = [spike64_hart()]
+    openocd_config_path = "spike.cfg"
 
     def create(self):
         return testlib.Spike(self)
index 95ddcfd6273a598a6aecf52e0000b0aac8e8a514..170de400df202221b7834678bcb300d89f2ba10d 100644 (file)
@@ -1,9 +1,12 @@
 import targets
 
-class E300(targets.Target):
+class E300Hart(targets.Hart):
     xlen = 32
     ram = 0x80000000
-    ram_size = 16 * 1024
+    ram_size = 256 * 1024 * 1024
     instruction_hardware_breakpoint_count = 2
-    openocd_config_path = "Freedom.cfg"
     link_script_path = "Freedom.lds"
+
+class E300(targets.Target):
+    openocd_config_path = "Freedom.cfg"
+    harts = [E300Hart()]
index 91be2e8901778cb3c6ea65d0b7fd3623d67a8391..f9428d077650d62c3483dcfe987dd7810ea24216 100644 (file)
@@ -1,14 +1,17 @@
 import targets
 import testlib
 
-class E300Sim(targets.Target):
+class E300Hart(targets.Hart):
     xlen = 32
-    timeout_sec = 6000
     ram = 0x80000000
     ram_size = 256 * 1024 * 1024
     instruction_hardware_breakpoint_count = 2
-    openocd_config_path = "Freedom.cfg"
     link_script_path = "Freedom.lds"
 
+class E300Sim(targets.Target):
+    timeout_sec = 6000
+    openocd_config_path = "Freedom.cfg"
+    harts = [E300Hart()]
+
     def create(self):
         return testlib.VcsSim(sim_cmd=self.sim_cmd, debug=False)
index c22aa4c6907fd3b280550e812f558dc05b627812..6da3ac509c5b53c7d75034983404b19159d6a945 100644 (file)
@@ -1,9 +1,12 @@
 import targets
 
-class U500(targets.Target):
+class U500Hart(targets.Hart):
     xlen = 64
     ram = 0x80000000
     ram_size = 16 * 1024
     instruction_hardware_breakpoint_count = 2
-    openocd_config_path = "Freedom.cfg"
     link_script_path = "Freedom.lds"
+
+class U500(targets.Target):
+    openocd_config_path = "Freedom.cfg"
+    harts = [U500Hart()]
index 62bc8278ab6efd20f30e9ce8c324883aab89d5a1..5c500c4842c7a6009a5dca06576d72a19843a133 100644 (file)
@@ -1,14 +1,17 @@
 import targets
 import testlib
 
-class U500Sim(targets.Target):
+class U500Hart(targets.Hart):
     xlen = 64
-    timeout_sec = 6000
     ram = 0x80000000
     ram_size = 256 * 1024 * 1024
     instruction_hardware_breakpoint_count = 2
-    openocd_config_path = "Freedom.cfg"
     link_script_path = "Freedom.lds"
 
-    def create(self):
+class U500Sim(Target):
+    timeout_sec = 6000
+    openocd_config_path = "Freedom.cfg"
+    harts = [U500Hart()]
+
+    def target(self):
         return testlib.VcsSim(sim_cmd=self.sim_cmd, debug=False)
index 813829e415116ec15a76c463924f168c14f83c87..3cb508cd0e1e358797ce7f836ffc6fdcc5046ad7 100644 (file)
@@ -1,8 +1,11 @@
 import targets
 
-class HiFive1(targets.Target):
+class HiFive1Hart(targets.Hart):
     xlen = 32
     ram = 0x80000000
     ram_size = 16 * 1024
     instruction_hardware_breakpoint_count = 2
     misa = 0x40001105
+
+class HiFive1(targets.Target):
+    harts = [HiFive1Hart()]
index b76f320a7071cc4fb00c02cd0aaacade8066c9e2..d6c7f8a0c0dd3b3e545abdb68ba2ffc14a36a940 100644 (file)
@@ -36,13 +36,12 @@ def compile(args, xlen=32): # pylint: disable=redefined-builtin
             cmd.append(found)
         else:
             cmd.append(arg)
+    header("Compile")
+    print "+", " ".join(cmd)
     process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
     stdout, stderr = process.communicate()
     if process.returncode:
-        print
-        header("Compile failed")
-        print "+", " ".join(cmd)
         print stdout,
         print stderr,
         header("")
@@ -63,16 +62,34 @@ class Spike(object):
     def __init__(self, target, halted=False, timeout=None, with_jtag_gdb=True):
         """Launch spike. Return tuple of its process and the port it's running
         on."""
+        self.process = None
+
+        if target.harts:
+            harts = target.harts
+        else:
+            harts = [target]
+
         if target.sim_cmd:
             cmd = shlex.split(target.sim_cmd)
         else:
             spike = os.path.expandvars("$RISCV/bin/spike")
             cmd = [spike]
-        if target.xlen == 32:
+
+        cmd += ["-p%d" % len(harts)]
+
+        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"]
         else:
             cmd += ["--isa", "RV64G"]
-        cmd += ["-m0x%x:0x%x" % (target.ram, target.ram_size)]
+
+        assert len(set(t.ram for t in harts)) == 1, \
+                "All spike harts must have the same RAM layout"
+        assert len(set(t.ram_size for t in harts)) == 1, \
+                "All spike harts must have the same RAM layout"
+        cmd += ["-m0x%x:0x%x" % (harts[0].ram, harts[0].ram_size)]
 
         if timeout:
             cmd = ["timeout", str(timeout)] + cmd
@@ -82,7 +99,7 @@ class Spike(object):
         if with_jtag_gdb:
             cmd += ['--rbb-port', '0']
             os.environ['REMOTE_BITBANG_HOST'] = 'localhost'
-        self.infinite_loop = target.compile(
+        self.infinite_loop = harts[0].compile(
                 "programs/checksum.c", "programs/tiny-malloc.c",
                 "programs/infinite_loop.S", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
         cmd.append(self.infinite_loop)
@@ -106,11 +123,12 @@ class Spike(object):
                     "connection"
 
     def __del__(self):
-        try:
-            self.process.kill()
-            self.process.wait()
-        except OSError:
-            pass
+        if self.process:
+            try:
+                self.process.kill()
+                self.process.wait()
+            except OSError:
+                pass
 
     def wait(self, *args, **kwargs):
         return self.process.wait(*args, **kwargs)
@@ -291,6 +309,10 @@ class Gdb(object):
         # Force consistency.
         self.command("set print entry-values no")
 
+    def select_hart(self, hart):
+        output = self.command("thread %d" % (hart.index + 1))
+        assert "Unknown" not in output
+
     def wait(self):
         """Wait for prompt."""
         self.child.expect(r"\(gdb\)")
@@ -391,19 +413,20 @@ def run_all_tests(module, target, parsed):
     gdb_cmd = parsed.gdb
 
     todo = []
-    if parsed.misaval:
-        target.misa = int(parsed.misaval, 16)
-        print "Using $misa from command line: 0x%x" % target.misa
-    elif target.misa:
-        print "Using $misa from target definition: 0x%x" % target.misa
-    else:
-        todo.append(("ExamineTarget", ExamineTarget))
+    for hart in target.harts:
+        if parsed.misaval:
+            hart.misa = int(parsed.misaval, 16)
+            print "Using $misa from command line: 0x%x" % hart.misa
+        elif hart.misa:
+            print "Using $misa from hart definition: 0x%x" % hart.misa
+        else:
+            todo.append(("ExamineTarget", ExamineTarget, hart))
 
     for name in dir(module):
         definition = getattr(module, name)
         if type(definition) == type and hasattr(definition, 'test') and \
                 (not parsed.test or any(test in name for test in parsed.test)):
-            todo.append((name, definition))
+            todo.append((name, definition, None))
 
     results, count = run_tests(parsed, target, todo)
 
@@ -417,12 +440,12 @@ def run_tests(parsed, target, todo):
     results = {}
     count = 0
 
-    for name, definition in todo:
-        instance = definition(target)
+    for name, definition, hart in todo:
         log_name = os.path.join(parsed.logs, "%s-%s-%s.log" %
                 (time.strftime("%Y%m%d-%H%M%S"), type(target).__name__, name))
         log_fd = open(log_name, 'w')
         print "Running %s > %s ..." % (name, log_name),
+        instance = definition(target, hart)
         sys.stdout.flush()
         log_fd.write("Test: %s\n" % name)
         log_fd.write("Target: %s\n" % type(target).__name__)
@@ -491,8 +514,12 @@ def print_log(path):
 class BaseTest(object):
     compiled = {}
 
-    def __init__(self, target):
+    def __init__(self, target, hart=None):
         self.target = target
+        if hart:
+            self.hart = hart
+        else:
+            self.hart = random.choice(target.harts)
         self.server = None
         self.target_process = None
         self.binary = None
@@ -514,7 +541,7 @@ class BaseTest(object):
             if compile_args not in BaseTest.compiled:
                 # pylint: disable=star-args
                 BaseTest.compiled[compile_args] = \
-                        self.target.compile(*compile_args)
+                        self.hart.compile(*compile_args)
         self.binary = BaseTest.compiled.get(compile_args)
 
     def classSetup(self):
@@ -581,8 +608,8 @@ class BaseTest(object):
 
 gdb_cmd = None
 class GdbTest(BaseTest):
-    def __init__(self, target):
-        BaseTest.__init__(self, target)
+    def __init__(self, target, hart=None):
+        BaseTest.__init__(self, target, hart=hart)
         self.gdb = None
 
     def classSetup(self):
@@ -598,15 +625,12 @@ class GdbTest(BaseTest):
         if self.binary:
             self.gdb.command("file %s" % self.binary)
         if self.target:
-            self.gdb.command("set arch riscv:rv%d" % self.target.xlen)
+            self.gdb.command("set arch riscv:rv%d" % self.hart.xlen)
             self.gdb.command("set remotetimeout %d" % self.target.timeout_sec)
         if self.server.port:
             self.gdb.command(
                     "target extended-remote localhost:%d" % self.server.port)
-            # Select a random thread.
-            # TODO: Allow a command line option to force a specific thread.
-            thread = random.choice(self.gdb.threads())
-            self.gdb.thread(thread)
+            self.gdb.select_hart(self.hart)
 
         for cmd in self.target.gdb_setup:
             self.gdb.command(cmd)
@@ -618,6 +642,17 @@ class GdbTest(BaseTest):
         del self.gdb
         BaseTest.classTeardown(self)
 
+class GdbSingleHartTest(GdbTest):
+    def classSetup(self):
+        GdbTest.classSetup(self)
+
+        for hart in self.target.harts:
+            # Park all harts that we're not using in a safe place.
+            if hart != self.hart:
+                self.gdb.select_hart(hart)
+                self.gdb.p("$pc=loop_forever")
+        self.gdb.select_hart(self.hart)
+
 class ExamineTarget(GdbTest):
     def test(self):
         self.target.misa = self.gdb.p("$misa")