Write test for downloading a mostly random program
authorTim Newsome <tim@sifive.com>
Tue, 10 May 2016 17:03:28 +0000 (10:03 -0700)
committerTim Newsome <tim@sifive.com>
Mon, 23 May 2016 19:12:12 +0000 (12:12 -0700)
It passes, but it's slow.

riscv/gdbserver.cc
tests/checksum.c [new file with mode: 0644]
tests/ebreak.py
tests/gdbserver.py
tests/mprv.S
tests/standalone.lds
tests/start.S [new file with mode: 0644]
tests/testlib.py

index 03df123d33e2f7bc55223611d46355e04dd57209..7c9461576973662923e5f2bc5e28a1bc8dad9c99 100644 (file)
@@ -729,6 +729,11 @@ class memory_write_op_t : public operation_t
         return false;
       }
 
+      if (gs.read_debug_ram(DEBUG_RAM_SIZE / 4 - 1)) {
+        fprintf(stderr, "Exception happened while writing to 0x%lx -> 0x%lx\n",
+            vaddr, paddr);
+      }
+
       offset += access_size;
       if (offset >= length) {
         gs.send_packet("OK");
@@ -1509,8 +1514,8 @@ void gdbserver_t::handle_packet(const std::vector<uint8_t> &packet)
       return handle_halt_reason(packet);
     case 'g':
       return handle_general_registers_read(packet);
-    case 'k':
-      return handle_kill(packet);
+//    case 'k':
+//      return handle_kill(packet);
     case 'm':
       return handle_memory_read(packet);
 //    case 'M':
diff --git a/tests/checksum.c b/tests/checksum.c
new file mode 100644 (file)
index 0000000..36152fc
--- /dev/null
@@ -0,0 +1,47 @@
+#include <stdint.h>
+
+// CRC code from http://www.hackersdelight.org/hdcodetxt/crc.c.txt
+
+// Reverses (reflects) bits in a 32-bit word.
+unsigned reverse(unsigned x) {
+   x = ((x & 0x55555555) <<  1) | ((x >>  1) & 0x55555555);
+   x = ((x & 0x33333333) <<  2) | ((x >>  2) & 0x33333333);
+   x = ((x & 0x0F0F0F0F) <<  4) | ((x >>  4) & 0x0F0F0F0F);
+   x = (x << 24) | ((x & 0xFF00) << 8) |
+       ((x >> 8) & 0xFF00) | (x >> 24);
+   return x;
+}
+
+// ----------------------------- crc32a --------------------------------
+
+/* This is the basic CRC algorithm with no optimizations. It follows the
+logic circuit as closely as possible. */
+
+unsigned int crc32a(uint8_t *message, unsigned int size) {
+   int i, j;
+   unsigned int byte, crc;
+
+   i = 0;
+   crc = 0xFFFFFFFF;
+   while (i < size) {
+      byte = message[i];            // Get next byte.
+      byte = reverse(byte);         // 32-bit reversal.
+      for (j = 0; j <= 7; j++) {    // Do eight times.
+         if ((int)(crc ^ byte) < 0)
+              crc = (crc << 1) ^ 0x04C11DB7;
+         else crc = crc << 1;
+         byte = byte << 1;          // Ready next msg bit.
+      }
+      i = i + 1;
+   }
+   return reverse(~crc);
+}
+
+extern uint8_t *data;
+extern uint32_t length;
+
+uint32_t main()
+{
+  /* Compute a simple checksum. */
+  return crc32a(data, length);
+}
index 4b41f7db4fce20ba6b820f77b4227f1a9e354326..dd7e65878b7a9f19dc0e9634d7bb24c385fc8de7 100755 (executable)
@@ -12,13 +12,13 @@ class EbreakTest(unittest.TestCase):
 
     def test_noport(self):
         """Make sure that we can run past ebreak when --gdb-port isn't used."""
-        spike = testlib.spike(self.binary, with_gdb=False, timeout=10)
+        spike = testlib.Spike(self.binary, with_gdb=False, timeout=10)
         result = spike.wait()
         self.assertEqual(result, 0)
 
     def test_nogdb(self):
         """Make sure that we can run past ebreak when gdb isn't attached."""
-        spike, port = testlib.spike(self.binary, timeout=10)
+        spike = testlib.Spike(self.binary, timeout=10)
         result = spike.wait()
         self.assertEqual(result, 0)
 
index 6ca9af673089e03f40c1bc9fd009508da98ad0ae..5be657485e82d1ac89eaf29f10f346285247580c 100755 (executable)
@@ -5,18 +5,16 @@ import testlib
 import unittest
 import tempfile
 import time
+import random
+import binascii
 
 class InstantHaltTest(unittest.TestCase):
     def setUp(self):
         self.binary = testlib.compile("debug.c")
-        self.spike, self.port = testlib.spike(self.binary, halted=True)
+        self.spike = testlib.Spike(self.binary, halted=True)
         self.gdb = testlib.Gdb()
         self.gdb.command("file %s" % self.binary)
-        self.gdb.command("target extended-remote localhost:%d" % self.port)
-
-    def tearDown(self):
-        self.spike.kill()
-        self.spike.wait()
+        self.gdb.command("target extended-remote localhost:%d" % self.spike.port)
 
     def test_instant_halt(self):
         self.assertEqual(0x1000, self.gdb.p("$pc"))
@@ -40,14 +38,10 @@ class InstantHaltTest(unittest.TestCase):
 class DebugTest(unittest.TestCase):
     def setUp(self):
         self.binary = testlib.compile("debug.c")
-        self.spike, self.port = testlib.spike(self.binary, halted=False)
+        self.spike = testlib.Spike(self.binary, halted=False)
         self.gdb = testlib.Gdb()
         self.gdb.command("file %s" % self.binary)
-        self.gdb.command("target extended-remote localhost:%d" % self.port)
-
-    def tearDown(self):
-        self.spike.kill()
-        self.spike.wait()
+        self.gdb.command("target extended-remote localhost:%d" % self.spike.port)
 
     def test_turbostep(self):
         """Single step a bunch of times."""
@@ -117,14 +111,10 @@ class DebugTest(unittest.TestCase):
 class RegsTest(unittest.TestCase):
     def setUp(self):
         self.binary = testlib.compile("regs.s")
-        self.spike, self.port = testlib.spike(self.binary, halted=False)
+        self.spike = testlib.Spike(self.binary, halted=False)
         self.gdb = testlib.Gdb()
         self.gdb.command("file %s" % self.binary)
-        self.gdb.command("target extended-remote localhost:%d" % self.port)
-
-    def tearDown(self):
-        self.spike.kill()
-        self.spike.wait()
+        self.gdb.command("target extended-remote localhost:%d" % self.spike.port)
 
     def test_write_gprs(self):
         # Note a0 is missing from this list since it's used to hold the
@@ -168,20 +158,54 @@ class RegsTest(unittest.TestCase):
         self.assertEqual(9, self.gdb.p("$x1"))
         self.assertEqual(9, self.gdb.p("$csr1"))
 
+class DownloadTest(unittest.TestCase):
+    def setUp(self):
+        length = 2**16
+        fd = file("data.c", "w")
+        fd.write("#include <stdint.h>\n")
+        fd.write("uint32_t length = %d;\n" % length)
+        fd.write("uint8_t d[%d] = {\n" % length)
+        self.crc = 0
+        for _ in range(length / 16):
+            fd.write("  ");
+            for _ in range(16):
+                value = random.randrange(1<<8)
+                fd.write("%d, " % value)
+                self.crc = binascii.crc32("%c" % value, self.crc)
+            fd.write("\n");
+        fd.write("};\n");
+        fd.write("uint8_t *data = &d[0];\n");
+        fd.close()
+
+        self.binary = testlib.compile("checksum.c", "data.c", "start.S",
+                "-mcmodel=medany",
+                "-T", "standalone.lds",
+                "-nostartfiles"
+                )
+        self.spike = testlib.Spike(None, halted=True)
+        self.gdb = testlib.Gdb()
+        self.gdb.command("file %s" % self.binary)
+        self.gdb.command("target extended-remote localhost:%d" % self.spike.port)
+
+    def test_download(self):
+        output = self.gdb.command("load")
+        self.assertNotIn("failed", output)
+        self.assertIn("Transfer rate", output)
+        self.gdb.command("b done")
+        self.gdb.c()
+        result = self.gdb.p("$a0")
+        self.assertEqual(self.crc, result)
+
 class MprvTest(unittest.TestCase):
     def setUp(self):
         self.binary = testlib.compile("mprv.S", "-T", "standalone.lds",
                 "-nostartfiles")
-        self.spike, self.port = testlib.spike(None, halted=True)
+        self.spike = testlib.Spike(None, halted=True)
         self.gdb = testlib.Gdb()
         self.gdb.command("file %s" % self.binary)
-        self.gdb.command("target extended-remote localhost:%d" % self.port)
+        self.gdb.command("target extended-remote localhost:%d" % self.spike.port)
         self.gdb.command("load")
 
-    def tearDown(self):
-        self.spike.kill()
-        self.spike.wait()
-
     def test_mprv(self):
         """Test that the debugger can access memory when MPRV is set."""
         self.gdb.c(wait=False)
index 114918ac59fa84021a3b857bd1fb8a015573cffc..df346b3504ddc291ed3282424b2a0278c9a64f5f 100644 (file)
@@ -1,10 +1,10 @@
 #include "../riscv/encoding.h"
 #define PGSHIFT         12
 
-        .global         main
+        .global         _start
 
         .section        .text
-main:
+_start:
         # Set up a page table entry that maps 0x0... to 0x8...
         la      t0, page_table
         srli    t0, t0, PGSHIFT
index 6705cfdc821495278b55999cbdfe5c8b9399fcac..9b1bab1f6ee9268e46ce74c7f3543763bc951cc4 100644 (file)
@@ -1,6 +1,6 @@
 OUTPUT_ARCH( "riscv" )
 
-ENTRY( main )
+ENTRY( _start )
 
 SECTIONS
 {
diff --git a/tests/start.S b/tests/start.S
new file mode 100644 (file)
index 0000000..76c37bb
--- /dev/null
@@ -0,0 +1,12 @@
+        .global         _start
+
+_start:
+        la      sp, stack_end
+        jal     main
+done:
+        j       done
+
+        .data
+stack:
+        .fill   4096, 1, 0
+stack_end:
index 3cba8c77e181d304dbe48cacc7fdb7be869268e8..d5e8d795c9e5370a515979a4512551d0bace1243 100644 (file)
@@ -40,28 +40,35 @@ def unused_port():
     s.close()
     return port
 
-def spike(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:
-        port = unused_port()
-        cmd += ['--gdb-port', str(port)]
-    cmd.append('pk')
-    if binary:
-        cmd.append(binary)
-    logfile = open("spike.log", "w")
-    process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile,
-            stderr=logfile)
-    if with_gdb:
-        return process, port
-    else:
-        return process
+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):
@@ -78,10 +85,10 @@ class Gdb(object):
         """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):