Made some progress towards working with spike.
authorTim Newsome <tim@sifive.com>
Tue, 7 Jun 2016 23:59:26 +0000 (16:59 -0700)
committerTim Newsome <tim@sifive.com>
Tue, 19 Jul 2016 01:51:54 +0000 (18:51 -0700)
I'm writing all the tests so they should just work on real hardware,
too.

debug/gdbserver.py
debug/programs/checksum.c
debug/programs/debug.c
debug/programs/init.c
debug/programs/regs.S
debug/targets/spike/entry.S [new file with mode: 0755]
debug/targets/spike/link.lds [new file with mode: 0755]
debug/testlib.py

index 8b7b562b21fbea947c17aef497f7004601f1b190..88c565765c4bffe174b523c9b8a551f4b2b29c7c 100755 (executable)
@@ -14,9 +14,21 @@ class DeleteServer(unittest.TestCase):
     def tearDown(self):
         del self.server
 
+class MemoryTest(DeleteServer):
+    def setUp(self):
+        self.server = target.server()
+        self.gdb = testlib.Gdb()
+        self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+
+    def test_32(self):
+        self.gdb.p("*((int*)0x%x) = 0x8675309" % target.ram)
+        self.gdb.p("*((int*)0x%x) = 0xdeadbeef" % (target.ram + 4))
+        self.assertEqual(self.gdb.p("*((int*)0x%x)" % target.ram), 0x8675309)
+        self.assertEqual(self.gdb.p("*((int*)0x%x)" % (target.ram + 4)), 0xdeadbeef)
+
 class InstantHaltTest(DeleteServer):
     def setUp(self):
-        self.binary = target.compile("debug.c")
+        self.binary = target.compile("programs/debug.c")
         self.server = target.server(self.binary, halted=True)
         self.gdb = testlib.Gdb()
         self.gdb.command("file %s" % self.binary)
@@ -43,11 +55,19 @@ class InstantHaltTest(DeleteServer):
 
 class DebugTest(DeleteServer):
     def setUp(self):
-        self.binary = target.compile("debug.c")
-        self.server = target.server(self.binary, halted=False)
+        self.binary = target.compile("programs/debug.c", "programs/checksum.c")
+        self.server = target.server()
         self.gdb = testlib.Gdb()
         self.gdb.command("file %s" % self.binary)
         self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+        self.gdb.load(self.binary)
+        self.gdb.b("_exit")
+
+    def exit(self):
+        output = self.gdb.c()
+        self.assertIn("Breakpoint", output)
+        self.assertIn("_exit", output)
+        self.assertEqual(self.gdb.p("status"), 0xc86455d4)
 
     def test_turbostep(self):
         """Single step a bunch of times."""
@@ -60,26 +80,19 @@ class DebugTest(DeleteServer):
             last_pc = pc
 
     def test_exit(self):
-        self.gdb.command("p i=0");
-        output = self.gdb.command("c")
-        self.assertIn("Continuing", output)
-        self.assertIn("Remote connection closed", output)
+        self.exit()
 
     def test_breakpoint(self):
-        self.gdb.command("p i=0");
-        self.gdb.command("b print_row")
-        # The breakpoint should be hit exactly 10 times.
-        for i in range(10):
-            output = self.gdb.command("c")
-            self.assertIn("Continuing", output)
-            self.assertIn("length=%d" % i, output)
-            self.assertIn("Breakpoint 1", output)
-        output = self.gdb.command("c")
-        self.assertIn("Continuing", output)
-        self.assertIn("Remote connection closed", output)
+        self.gdb.b("rot13")
+        # The breakpoint should be hit exactly 2 times.
+        for i in range(2):
+            output = self.gdb.c()
+            self.assertIn("Breakpoint ", output)
+        self.exit()
 
     def test_registers(self):
-        self.gdb.command("p i=0");
+        self.gdb.b("rot13")
+        self.gdb.c()
         # Try both forms to test gdb.
         for cmd in ("info all-registers", "info registers all"):
             output = self.gdb.command(cmd)
@@ -100,19 +113,20 @@ class DebugTest(DeleteServer):
             last_instret = instret
             self.gdb.command("stepi")
 
+        self.exit()
+
     def test_interrupt(self):
         """Sending gdb ^C while the program is running should cause it to halt."""
-        self.gdb.c(wait=False)
-        time.sleep(0.1)
-        self.gdb.interrupt()
+        self.gdb.b("main:start")
+        self.gdb.c()
         self.gdb.command("p i=123");
         self.gdb.c(wait=False)
         time.sleep(0.1)
-        self.gdb.interrupt()
-        self.gdb.command("p i=0");
-        output = self.gdb.c()
-        self.assertIn("Continuing", output)
-        self.assertIn("Remote connection closed", output)
+        output = self.gdb.interrupt()
+        assert "main" in output
+        self.assertGreater(self.gdb.p("j"), 10)
+        self.gdb.p("i=0");
+        self.exit()
 
 class RegsTest(DeleteServer):
     def setUp(self):
@@ -169,6 +183,14 @@ class DownloadTest(DeleteServer):
     def setUp(self):
         length = 2**20
         fd = file("data.c", "w")
+# extern uint8_t *data;
+# extern uint32_t length;
+# 
+# uint32_t main()
+#{
+#  /* Compute a simple checksum. */
+#  return crc32a(data, length);
+#}
         fd.write("#include <stdint.h>\n")
         fd.write("uint32_t length = %d;\n" % length)
         fd.write("uint8_t d[%d] = {\n" % length)
@@ -225,20 +247,28 @@ class Target(object):
         raise NotImplementedError
 
     def compile(self, *sources):
-        return testlib.compile(*(sources +
+        return testlib.compile(sources +
                 ("targets/%s/entry.S" % self.name, "programs/init.c",
                     "-I", "../env",
                     "-T", "targets/%s/link.lds" % self.name,
-                    "-nostartfiles")))
+                    "-nostartfiles",
+                    "-mcmodel=medany"), xlen=self.xlen)
 
 class SpikeTarget(Target):
     name = "spike"
+    xlen = 64
+    ram = 0x80010000
+
+    def server(self):
+        return testlib.Spike(parsed.cmd, halted=True)
 
 class MicroSemiTarget(Target):
     name = "m2gl_m2s"
+    xlen = 32
+    ram = 0x80000000
 
     def server(self):
-        return testlib.Openocd(cmd=parsed.openocd,
+        return testlib.Openocd(cmd=parsed.cmd,
                 config="targets/%s/openocd.cfg" % self.name)
 
 targets = [
@@ -252,8 +282,8 @@ def main():
     for t in targets:
         group.add_argument("--%s" % t.name, action="store_const", const=t,
                 dest="target")
-    parser.add_argument("--openocd", help="The OpenOCD command to use.",
-            default="openocd")
+    parser.add_argument("--cmd",
+            help="The command to use to start the debug server.")
     parser.add_argument("unittest", nargs="*")
     global parsed
     parsed = parser.parse_args()
index 36152fc085cd61f4f3b31e02d01e536d30c357ad..e076f8a1f0b893737036f774bb017880f1e0bb6b 100644 (file)
@@ -36,12 +36,3 @@ unsigned int crc32a(uint8_t *message, unsigned int size) {
    }
    return reverse(~crc);
 }
-
-extern uint8_t *data;
-extern uint32_t length;
-
-uint32_t main()
-{
-  /* Compute a simple checksum. */
-  return crc32a(data, length);
-}
index 2cad88f089bcb4fa271dd7e341968d894b0c6eef..afca484e992d1229eb7664ab5b2326dce8ea76da 100644 (file)
@@ -1,27 +1,46 @@
 #include <stdio.h>
+#include <string.h>
+#include <stdint.h>
 
-char c = 'x';
+unsigned int crc32a(uint8_t *message, unsigned int size);
 
-void print_row(int length)
+void rot13(char *buf)
 {
-    for (int x=0; x<length; x++) {
-        printf("%c", c);
+    while (*buf) {
+        if ((*buf >= 'a' && *buf <= 'm') ||
+                (*buf >= 'A' && *buf <= 'M')) {
+            *buf += 13;
+        } else if ((*buf >= 'n' && *buf <= 'z') ||
+                (*buf >= 'N' && *buf <= 'Z')) {
+            *buf -= 13;
+        }
+        buf++;
     }
-    printf("\n");
+}
+
+size_t strlen(const char *buf)
+{
+    int len = 0;
+    while (buf[len])
+        len++;
+    return len;
 }
 
 int main()
 {
-    volatile int i = 42;
-    const char *text = "constant\n";
-    int threshold = 7;
+    volatile int i = 0;
+    int j = 0;
+    char *fox = "The quick brown fox jumps of the lazy dog.";
+    unsigned int checksum = 0;
 
-    // Wait for the debugger to get us out of this loop.
+start:
     while (i)
-        ;
+        j++;
 
-    printf("%s", text);
-    for (int y=0; y < 10; y++) {
-        print_row(y);
-    }
+    rot13(fox);
+    checksum ^= crc32a(fox, strlen(fox));
+    rot13(fox);
+    checksum ^= crc32a(fox, strlen(fox));
+
+    return checksum;
 }
index 074bc2178de4ca96a0152186099d2d86339aa8de..a2b41b02d60e2eb51af3d9322dcedb2f72460c67 100644 (file)
@@ -6,9 +6,19 @@ void handle_trap(unsigned int mcause, unsigned int mepc, unsigned int sp)
         ;
 }
 
-void _init()
+void _exit(int status)
 {
-    main();
+    // Make sure gcc doesn't inline _exit, so we can actually set a breakpoint
+    // on it.
+    volatile int i = 42;
+    while (i)
+        ;
+    // _exit isn't supposed to return.
     while (1)
         ;
 }
+
+void _init()
+{
+    _exit(main());
+}
index e6456e14111a329aabedc4a4467f90d98c8ccf74..54b39616b9c542d2196516c1a38a99aa513591ef 100644 (file)
@@ -1,38 +1,48 @@
+#ifdef __riscv64
+# define LREG ld
+# define SREG sd
+# define REGBYTES 8
+#else
+# define LREG lw
+# define SREG sw
+# define REGBYTES 4
+#endif
+
         .global main
 main:
         j       main
 
 write_regs:
-        sd      x1, 0(a0)
-        sd      x2, 8(a0)
-        sd      x3, 16(a0)
-        sd      x4, 24(a0)
-        sd      x5, 32(a0)
-        sd      x6, 40(a0)
-        sd      x7, 48(a0)
-        sd      x8, 56(a0)
-        sd      x9, 64(a0)
-        sd      x11, 72(a0)
-        sd      x12, 80(a0)
-        sd      x13, 88(a0)
-        sd      x14, 96(a0)
-        sd      x15, 104(a0)
-        sd      x16, 112(a0)
-        sd      x17, 120(a0)
-        sd      x18, 128(a0)
-        sd      x19, 136(a0)
-        sd      x20, 144(a0)
-        sd      x21, 152(a0)
-        sd      x22, 160(a0)
-        sd      x23, 168(a0)
-        sd      x24, 176(a0)
-        sd      x25, 184(a0)
-        sd      x26, 192(a0)
-        sd      x27, 200(a0)
-        sd      x28, 208(a0)
-        sd      x29, 216(a0)
-        sd      x30, 224(a0)
-        sd      x31, 232(a0)
+        SREG    x1, 0(a0)
+        SREG    x2, 8(a0)
+        SREG    x3, 16(a0)
+        SREG    x4, 24(a0)
+        SREG    x5, 32(a0)
+        SREG    x6, 40(a0)
+        SREG    x7, 48(a0)
+        SREG    x8, 56(a0)
+        SREG    x9, 64(a0)
+        SREG    x11, 72(a0)
+        SREG    x12, 80(a0)
+        SREG    x13, 88(a0)
+        SREG    x14, 96(a0)
+        SREG    x15, 104(a0)
+        SREG    x16, 112(a0)
+        SREG    x17, 120(a0)
+        SREG    x18, 128(a0)
+        SREG    x19, 136(a0)
+        SREG    x20, 144(a0)
+        SREG    x21, 152(a0)
+        SREG    x22, 160(a0)
+        SREG    x23, 168(a0)
+        SREG    x24, 176(a0)
+        SREG    x25, 184(a0)
+        SREG    x26, 192(a0)
+        SREG    x27, 200(a0)
+        SREG    x28, 208(a0)
+        SREG    x29, 216(a0)
+        SREG    x30, 224(a0)
+        SREG    x31, 232(a0)
 
         csrr    x1, 1   # fflags
 
diff --git a/debug/targets/spike/entry.S b/debug/targets/spike/entry.S
new file mode 100755 (executable)
index 0000000..ff49cf6
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef ENTRY_S
+#define ENTRY_S
+
+#include "encoding.h"
+
+#define STACK_SIZE ((1 << 12) - 128)
+
+#ifdef __riscv64
+# define LREG ld
+# define SREG sd
+# define REGBYTES 8
+#else
+# define LREG lw
+# define SREG sw
+# define REGBYTES 4
+#endif
+
+  .section      .text.entry
+  .globl _start
+_start:
+  j handle_reset
+
+nmi_vector:
+  j nmi_vector
+
+trap_vector:
+  j trap_entry
+
+handle_reset:
+  la t0, trap_entry
+  csrw mtvec, t0
+  csrwi mstatus, 0
+  csrwi mideleg, 0
+  csrwi medeleg, 0
+  csrwi mie, 0
+
+  # initialize global pointer
+  la gp, _gp
+
+  # initialize stack pointer
+  la sp, stack_top
+
+  # perform the rest of initialization in C
+  j _init
+
+trap_entry:
+  addi sp, sp, -32*REGBYTES
+
+  SREG x1, 1*REGBYTES(sp)
+  SREG x2, 2*REGBYTES(sp)
+  SREG x3, 3*REGBYTES(sp)
+  SREG x4, 4*REGBYTES(sp)
+  SREG x5, 5*REGBYTES(sp)
+  SREG x6, 6*REGBYTES(sp)
+  SREG x7, 7*REGBYTES(sp)
+  SREG x8, 8*REGBYTES(sp)
+  SREG x9, 9*REGBYTES(sp)
+  SREG x10, 10*REGBYTES(sp)
+  SREG x11, 11*REGBYTES(sp)
+  SREG x12, 12*REGBYTES(sp)
+  SREG x13, 13*REGBYTES(sp)
+  SREG x14, 14*REGBYTES(sp)
+  SREG x15, 15*REGBYTES(sp)
+  SREG x16, 16*REGBYTES(sp)
+  SREG x17, 17*REGBYTES(sp)
+  SREG x18, 18*REGBYTES(sp)
+  SREG x19, 19*REGBYTES(sp)
+  SREG x20, 20*REGBYTES(sp)
+  SREG x21, 21*REGBYTES(sp)
+  SREG x22, 22*REGBYTES(sp)
+  SREG x23, 23*REGBYTES(sp)
+  SREG x24, 24*REGBYTES(sp)
+  SREG x25, 25*REGBYTES(sp)
+  SREG x26, 26*REGBYTES(sp)
+  SREG x27, 27*REGBYTES(sp)
+  SREG x28, 28*REGBYTES(sp)
+  SREG x29, 29*REGBYTES(sp)
+  SREG x30, 30*REGBYTES(sp)
+  SREG x31, 31*REGBYTES(sp)
+
+  csrr a0, mcause
+  csrr a1, mepc
+  mv a2, sp
+  jal handle_trap
+  csrw mepc, a0
+
+  # Remain in M-mode after mret
+  li t0, MSTATUS_MPP
+  csrs mstatus, t0
+
+  LREG x1, 1*REGBYTES(sp)
+  LREG x2, 2*REGBYTES(sp)
+  LREG x3, 3*REGBYTES(sp)
+  LREG x4, 4*REGBYTES(sp)
+  LREG x5, 5*REGBYTES(sp)
+  LREG x6, 6*REGBYTES(sp)
+  LREG x7, 7*REGBYTES(sp)
+  LREG x8, 8*REGBYTES(sp)
+  LREG x9, 9*REGBYTES(sp)
+  LREG x10, 10*REGBYTES(sp)
+  LREG x11, 11*REGBYTES(sp)
+  LREG x12, 12*REGBYTES(sp)
+  LREG x13, 13*REGBYTES(sp)
+  LREG x14, 14*REGBYTES(sp)
+  LREG x15, 15*REGBYTES(sp)
+  LREG x16, 16*REGBYTES(sp)
+  LREG x17, 17*REGBYTES(sp)
+  LREG x18, 18*REGBYTES(sp)
+  LREG x19, 19*REGBYTES(sp)
+  LREG x20, 20*REGBYTES(sp)
+  LREG x21, 21*REGBYTES(sp)
+  LREG x22, 22*REGBYTES(sp)
+  LREG x23, 23*REGBYTES(sp)
+  LREG x24, 24*REGBYTES(sp)
+  LREG x25, 25*REGBYTES(sp)
+  LREG x26, 26*REGBYTES(sp)
+  LREG x27, 27*REGBYTES(sp)
+  LREG x28, 28*REGBYTES(sp)
+  LREG x29, 29*REGBYTES(sp)
+  LREG x30, 30*REGBYTES(sp)
+  LREG x31, 31*REGBYTES(sp)
+
+  addi sp, sp, 32*REGBYTES
+  mret
+
+  .bss
+  .align 4
+stack_bottom:
+  .skip STACK_SIZE
+stack_top:
+#endif
diff --git a/debug/targets/spike/link.lds b/debug/targets/spike/link.lds
new file mode 100755 (executable)
index 0000000..029a332
--- /dev/null
@@ -0,0 +1,35 @@
+OUTPUT_ARCH( "riscv" )
+
+SECTIONS
+{
+  /* Leave some space for pk's data structures, which includes tohost/fromhost
+   * which are special addresses we ought to leave alone.  */
+  . = 0x80010000;
+  .text : 
+  {
+    *(.text.entry)
+    *(.text)
+  }
+
+  /* data segment */
+  .data : { *(.data) }
+
+  .sdata : {
+    _gp = . + 0x800;
+    *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2)
+    *(.srodata*)
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+  }
+
+  /* bss segment */
+  .sbss : {
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+  }
+  .bss : { *(.bss) }
+
+  /* End of uninitalized data segement */
+  _end = .;
+  _heap_end = .;
+}
+
index 0902983c88a41a7c03a986ca86dceea776bbef6a..d8e485aa3385ed1829041da73f0d2064151ca144 100644 (file)
@@ -16,11 +16,11 @@ def find_file(path):
             return fullpath
     return None
 
-def compile(*args):
+def compile(args, xlen=32):
     """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")
-    cmd = [cc, "-g", "-O", "-o", dst]
+    cc = os.path.expandvars("$RISCV/bin/riscv%d-unknown-elf-gcc" % xlen)
+    cmd = [cc, "-g", "-o", dst]
     for arg in args:
         found = find_file(arg)
         if found:
@@ -42,13 +42,16 @@ def unused_port():
     return port
 
 class Spike(object):
-    def __init__(self, binary, halted=False, with_gdb=True, timeout=None):
+    def __init__(self, cmd, binary=None, halted=False, with_gdb=True, timeout=None):
         """Launch spike. Return tuple of its process and the port it's running on."""
-        cmd = []
+        if cmd:
+            cmd = shlex.split(cmd)
+        else:
+            cmd = ["spike"]
+
         if timeout:
-            cmd += ["timeout", str(timeout)]
+            cmd = ["timeout", str(timeout)] + cmd
 
-        cmd += [find_file("spike")]
         if halted:
             cmd.append('-H')
         if with_gdb:
@@ -117,7 +120,9 @@ class Gdb(object):
 
     def c(self, wait=True):
         if wait:
-            return self.command("c")
+            output = self.command("c")
+            assert "Continuing" in output
+            return output
         else:
             self.child.sendline("c")
             self.child.expect("Continuing")
@@ -125,6 +130,7 @@ class Gdb(object):
     def interrupt(self):
         self.child.send("\003");
         self.child.expect("\(gdb\)")
+        return self.child.before.strip()
 
     def x(self, address, size='w'):
         output = self.command("x/%s %s" % (size, address))
@@ -132,9 +138,18 @@ class Gdb(object):
         return value
 
     def p(self, obj):
-        output = self.command("p %s" % obj)
-        value = int(output.split('=')[-1].strip())
+        output = self.command("p/x %s" % obj)
+        value = int(output.split('=')[-1].strip(), 0)
         return value
 
     def stepi(self):
         return self.command("stepi")
+
+    def load(self, binary):
+        return self.command("load %s" % binary)
+
+    def b(self, location):
+        output = self.command("b %s" % location)
+        assert "not defined" not in output
+        assert "Breakpoint" in output
+        return output