Use compressed code if the target supports it.
authorTim Newsome <tim@sifive.com>
Tue, 27 Dec 2016 19:34:50 +0000 (11:34 -0800)
committerTim Newsome <tim@sifive.com>
Tue, 27 Dec 2016 19:34:50 +0000 (11:34 -0800)
The main change was to read misa before running any other test. If misa
indicates C is supported, then use compressed code. This required
changing some tests, mostly to ensure correct alignment.  The single
step test also needs to know the correct addresses to step through in
compressed code.

Only print at most 1000 lines from each log file.

debug/gdbserver.py
debug/openocd.py
debug/programs/step.S
debug/programs/trigger.S
debug/targets.py
debug/testlib.py

index 323d32c2924e9b911af0e6b7c3b360fb9ec25f48..9dfea39fda3630004109ce484b845aa90e099087 100755 (executable)
@@ -11,6 +11,7 @@ import targets
 import testlib
 from testlib import assertEqual, assertNotEqual, assertIn
 from testlib import assertGreater, assertTrue, assertRegexpMatches, assertLess
 import testlib
 from testlib import assertEqual, assertNotEqual, assertIn
 from testlib import assertGreater, assertTrue, assertRegexpMatches, assertLess
+from testlib import GdbTest
 
 MSTATUS_UIE = 0x00000001
 MSTATUS_SIE = 0x00000002
 
 MSTATUS_UIE = 0x00000001
 MSTATUS_SIE = 0x00000002
@@ -34,30 +35,6 @@ MSTATUS64_SD = 0x8000000000000000
 
 # pylint: disable=abstract-method
 
 
 # pylint: disable=abstract-method
 
-def gdb(
-        target=None,
-        port=None,
-        binary=None
-        ):
-
-    g = None
-    if parsed.gdb:
-        g = testlib.Gdb(parsed.gdb)
-    else:
-        g = testlib.Gdb()
-
-    if binary:
-        g.command("file %s" % binary)
-    if target:
-        g.command("set arch riscv:rv%d" % target.xlen)
-        g.command("set remotetimeout %d" % target.timeout_sec)
-    if port:
-        g.command("target extended-remote localhost:%d" % port)
-
-    g.p("$priv=3")
-
-    return g
-
 def ihex_line(address, record_type, data):
     assert len(data) < 128
     line = ":%02X%04X%02X" % (len(data), address, record_type)
 def ihex_line(address, record_type, data):
     assert len(data) < 128
     line = ":%02X%04X%02X" % (len(data), address, record_type)
@@ -86,20 +63,6 @@ def ihex_parse(line):
 def readable_binary_string(s):
     return "".join("%02x" % ord(c) for c in s)
 
 def readable_binary_string(s):
     return "".join("%02x" % ord(c) for c in s)
 
-class GdbTest(testlib.BaseTest):
-    def __init__(self, target):
-        testlib.BaseTest.__init__(self, target)
-        self.gdb = None
-
-    def classSetup(self):
-        testlib.BaseTest.classSetup(self)
-        self.logs.append("gdb.log")
-        self.gdb = gdb(self.target, self.server.port, self.binary)
-
-    def classTeardown(self):
-        del self.gdb
-        testlib.BaseTest.classTeardown(self)
-
 class SimpleRegisterTest(GdbTest):
     def check_reg(self, name):
         a = random.randrange(1<<self.target.xlen)
 class SimpleRegisterTest(GdbTest):
     def check_reg(self, name):
         a = random.randrange(1<<self.target.xlen)
@@ -145,10 +108,10 @@ class SimpleF18Test(SimpleRegisterTest):
         self.gdb.stepi()
         assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - b), .001)
 
         self.gdb.stepi()
         assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - b), .001)
 
+    def early_applicable(self):
+        return self.target.extensionSupported('F')
+
     def test(self):
     def test(self):
-        misa = self.gdb.p("$misa")
-        if not misa & (1<<(ord('F')-ord('A'))):
-            return 'not_applicable'
         self.check_reg("f18")
 
 class SimpleMemoryTest(GdbTest):
         self.check_reg("f18")
 
 class SimpleMemoryTest(GdbTest):
@@ -440,10 +403,14 @@ class StepTest(GdbTest):
 
     def test(self):
         main_address = self.gdb.p("$pc")
 
     def test(self):
         main_address = self.gdb.p("$pc")
-        for expected in (4, 8, 0xc, 0x10, 0x18, 0x1c, 0x28, 0x20, 0x2c, 0x2c):
+        if self.target.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)
+        for expected in sequence:
             self.gdb.stepi()
             pc = self.gdb.p("$pc")
             self.gdb.stepi()
             pc = self.gdb.p("$pc")
-            assertEqual("%x" % pc, "%x" % (expected + main_address))
+            assertEqual("%x" % (pc - main_address), "%x" % expected)
 
 class TriggerTest(GdbTest):
     compile_args = ("programs/trigger.S", )
 
 class TriggerTest(GdbTest):
     compile_args = ("programs/trigger.S", )
@@ -656,7 +623,7 @@ class PrivTest(GdbTest):
         # pylint: disable=attribute-defined-outside-init
         self.gdb.load()
 
         # pylint: disable=attribute-defined-outside-init
         self.gdb.load()
 
-        misa = self.gdb.p("$misa")
+        misa = self.target.misa
         self.supported = set()
         if misa & (1<<20):
             self.supported.add(0)
         self.supported = set()
         if misa & (1<<20):
             self.supported.add(0)
@@ -710,8 +677,6 @@ def main():
             ./gdbserver.py --freedom-e300 --cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" Simple
             """)
     targets.add_target_options(parser)
             ./gdbserver.py --freedom-e300 --cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" Simple
             """)
     targets.add_target_options(parser)
-    parser.add_argument("--gdb",
-            help="The command to use to start gdb.")
 
     testlib.add_test_run_options(parser)
 
 
     testlib.add_test_run_options(parser)
 
@@ -725,7 +690,7 @@ def main():
 
     module = sys.modules[__name__]
 
 
     module = sys.modules[__name__]
 
-    return testlib.run_all_tests(module, target, parsed.test, parsed.fail_fast)
+    return testlib.run_all_tests(module, target, parsed)
 
 # TROUBLESHOOTING TIPS
 # If a particular test fails, run just that one test, eg.:
 
 # TROUBLESHOOTING TIPS
 # If a particular test fails, run just that one test, eg.:
index 0ab8810162ecaf2c8be2f76eee42d9c6ea6c44f3..50c6f3c9592914d10cede9d5d907fbc75db99d0c 100755 (executable)
@@ -79,7 +79,7 @@ def main():
 
     module = sys.modules[__name__]
 
 
     module = sys.modules[__name__]
 
-    return testlib.run_all_tests(module, target, parsed.test, parsed.fail_fast)
+    return testlib.run_all_tests(module, target, parsed)
 
 if __name__ == '__main__':
     sys.exit(main())
 
 if __name__ == '__main__':
     sys.exit(main())
index 6601548a5614fe158693dbbb3464a6f1b0dd9fc7..3e7b42e26c5dbbb517614d5cb2232700e7d239d4 100644 (file)
@@ -11,14 +11,18 @@ main:
         nop                             // 0x14
 one:
         beq     zero, t0, one           // 0x18
         nop                             // 0x14
 one:
         beq     zero, t0, one           // 0x18
-        jal     two                     // 0x1c
+        // Use t0 instead of ra to force a 32-bit opcode in C mode. Otherwise
+        // 32-bit and 64-bit binaries end up with different instructions (I
+        // didn't pursue this).
+        jal     t0, two                 // 0x1c
 
 three:
         .word   0                       // 0x20
         nop                             // 0x24
 
 two:
 
 three:
         .word   0                       // 0x20
         nop                             // 0x24
 
 two:
-        ret                             // 0x28
+        jr      t0                      // 0x28
 
 
+        .align  2
 trap_entry:
         j       trap_entry              // 0x2c
 trap_entry:
         j       trap_entry              // 0x2c
index d87b71e1fc35ef0f6f587b84075e68a9497f457a..ac5a2f8b8924c10d899aa29c1120a12317efaf8e 100644 (file)
@@ -101,6 +101,7 @@ read_triggers:
         ret
 
         .data
         ret
 
         .data
+        .align  3
 data:   .word   0x40
         .word   0x41
         .word   0x42
 data:   .word   0x40
         .word   0x41
         .word   0x42
index c431a671697a4560e0bf469535b89b1ee5ccb1c7..bcebc0b613ef83797a01fc4dc7d9d77370bf0223 100644 (file)
@@ -12,6 +12,7 @@ class Target(object):
     temporary_binary = None
     openocd_config = []
     use_fpu = False
     temporary_binary = None
     openocd_config = []
     use_fpu = False
+    misa = None
 
     def __init__(self, cmd, run, isolate):
         self.cmd = cmd
 
     def __init__(self, cmd, run, isolate):
         self.cmd = cmd
@@ -42,6 +43,8 @@ class Target(object):
         march = "rv%dima" % self.xlen
         if self.use_fpu:
             march += "fd"
         march = "rv%dima" % self.xlen
         if self.use_fpu:
             march += "fd"
+        if self.extensionSupported("c"):
+            march += "c"
         testlib.compile(sources +
                 ("programs/entry.S", "programs/init.c",
                     "-I", "../env",
         testlib.compile(sources +
                 ("programs/entry.S", "programs/init.c",
                     "-I", "../env",
@@ -54,6 +57,10 @@ class Target(object):
                 xlen=self.xlen)
         return binary_name
 
                 xlen=self.xlen)
         return binary_name
 
+    def extensionSupported(self, letter):
+        # target.misa is set by testlib.ExamineTarget
+        return self.misa & (1 << (ord(letter.upper()) - ord('A')))
+
 class SpikeTarget(Target):
     # pylint: disable=abstract-method
     directory = "spike"
 class SpikeTarget(Target):
     # pylint: disable=abstract-method
     directory = "spike"
index ac1d7713f9f5f63d319efd3dbbfb42f98d500c73..a762174df18240a2309d3f8def20f14ff998d3c5 100644 (file)
@@ -327,23 +327,31 @@ class Gdb(object):
         assert "Hardware assisted breakpoint" in output
         return output
 
         assert "Hardware assisted breakpoint" in output
         return output
 
-def run_all_tests(module, target, tests, fail_fast):
+def run_all_tests(module, target, parsed):
     good_results = set(('pass', 'not_applicable'))
 
     start = time.time()
 
     results = {}
     count = 0
     good_results = set(('pass', 'not_applicable'))
 
     start = time.time()
 
     results = {}
     count = 0
+
+    global gdb_cmd  # pylint: disable=global-statement
+    gdb_cmd = parsed.gdb
+
+    todo = [("ExamineTarget", ExamineTarget)]
     for name in dir(module):
         definition = getattr(module, name)
         if type(definition) == type and hasattr(definition, 'test') and \
     for name in dir(module):
         definition = getattr(module, name)
         if type(definition) == type and hasattr(definition, 'test') and \
-                (not tests or any(test in name for test in tests)):
-            instance = definition(target)
-            result = instance.run()
-            results.setdefault(result, []).append(name)
-            count += 1
-            if result not in good_results and fail_fast:
-                break
+                (not parsed.test or any(test in name for test in parsed.test)):
+            todo.append((name, definition))
+
+    for name, definition in todo:
+        instance = definition(target)
+        result = instance.run()
+        results.setdefault(result, []).append(name)
+        count += 1
+        if result not in good_results and parsed.fail_fast:
+            break
 
     header("ran %d tests in %.0fs" % (count, time.time() - start), dash=':')
 
 
     header("ran %d tests in %.0fs" % (count, time.time() - start), dash=':')
 
@@ -362,6 +370,8 @@ def add_test_run_options(parser):
             help="Exit as soon as any test fails.")
     parser.add_argument("test", nargs='*',
             help="Run only tests that are named here.")
             help="Exit as soon as any test fails.")
     parser.add_argument("test", nargs='*',
             help="Run only tests that are named here.")
+    parser.add_argument("--gdb",
+            help="The command to use to start gdb.")
 
 def header(title, dash='-'):
     if title:
 
 def header(title, dash='-'):
     if title:
@@ -372,9 +382,21 @@ def header(title, dash='-'):
     else:
         print dash * 40
 
     else:
         print dash * 40
 
+def print_log(path):
+    header(path)
+    lines = open(path, "r").readlines()
+    if len(lines) > 1000:
+        for l in lines[:500]:
+            sys.stdout.write(l)
+        print "..."
+        for l in lines[-500:]:
+            sys.stdout.write(l)
+    else:
+        for l in lines:
+            sys.stdout.write(l)
+
 class BaseTest(object):
     compiled = {}
 class BaseTest(object):
     compiled = {}
-    logs = []
 
     def __init__(self, target):
         self.target = target
 
     def __init__(self, target):
         self.target = target
@@ -382,6 +404,7 @@ class BaseTest(object):
         self.target_process = None
         self.binary = None
         self.start = 0
         self.target_process = None
         self.binary = None
         self.start = 0
+        self.logs = []
 
     def early_applicable(self):
         """Return a false value if the test has determined it cannot run
 
     def early_applicable(self):
         """Return a false value if the test has determined it cannot run
@@ -450,8 +473,7 @@ class BaseTest(object):
             header("Traceback")
             traceback.print_exc(file=sys.stdout)
             for log in self.logs:
             header("Traceback")
             traceback.print_exc(file=sys.stdout)
             for log in self.logs:
-                header(log)
-                print open(log, "r").read()
+                print_log(log)
             print "/" * 40
             return result
 
             print "/" * 40
             return result
 
@@ -463,6 +485,55 @@ class BaseTest(object):
         print "%s in %.2fs" % (result, time.time() - self.start)
         return result
 
         print "%s in %.2fs" % (result, time.time() - self.start)
         return result
 
+gdb_cmd = None
+class GdbTest(BaseTest):
+    def __init__(self, target):
+        BaseTest.__init__(self, target)
+        self.gdb = None
+
+    def classSetup(self):
+        BaseTest.classSetup(self)
+        self.logs.append("gdb.log")
+
+        if gdb_cmd:
+            self.gdb = Gdb(gdb_cmd)
+        else:
+            self.gdb = Gdb()
+
+        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 remotetimeout %d" % self.target.timeout_sec)
+        if self.server.port:
+            self.gdb.command(
+                    "target extended-remote localhost:%d" % self.server.port)
+
+        self.gdb.p("$priv=3")
+
+    def classTeardown(self):
+        del self.gdb
+        BaseTest.classTeardown(self)
+
+class ExamineTarget(GdbTest):
+    def test(self):
+        self.target.misa = self.gdb.p("$misa")
+
+        txt = "RV"
+        if (self.target.misa >> 30) == 1:
+            txt += "32"
+        elif (self.target.misa >> 62) == 2:
+            txt += "64"
+        elif (self.target.misa >> 126) == 3:
+            txt += "128"
+        else:
+            txt += "??"
+
+        for i in range(26):
+            if self.target.misa & (1<<i):
+                txt += chr(i + ord('A'))
+        print txt,
+
 class TestFailed(Exception):
     def __init__(self, message):
         Exception.__init__(self)
 class TestFailed(Exception):
     def __init__(self, message):
         Exception.__init__(self)