aboutsummaryrefslogtreecommitdiff
path: root/debug/targets.py
blob: 296b0a95f72929c621b334da11954a8b45dab7ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import importlib
import os.path
import sys
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
    # 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

    # 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 = []

    # Implements dmode in tdata1 as described in the spec. Targets 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

    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)

    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 compile(self, *sources):
        binary_name = "%s_%s-%d" % (
                self.name,
                os.path.basename(os.path.splitext(sources[0])[0]),
                self.xlen)
        if self.isolate:
            self.temporary_binary = tempfile.NamedTemporaryFile(
                    prefix=binary_name + "_")
            binary_name = self.temporary_binary.name
            Target.temporary_files.append(self.temporary_binary)
        march = "rv%dima" % self.xlen
        for letter in "fdc":
            if self.extensionSupported(letter):
                march += letter
        testlib.compile(sources +
                ("programs/entry.S", "programs/init.c",
                    "-I", "../env",
                    "-march=%s" % march,
                    "-T", self.link_script_path,
                    "-nostartfiles",
                    "-mcmodel=medany",
                    "-DXLEN=%d" % self.xlen,
                    "-o", binary_name),
                xlen=self.xlen)
        return binary_name

    def extensionSupported(self, letter):
        # target.misa is set by testlib.ExamineTarget
        if self.misa:
            return self.misa & (1 << (ord(letter.upper()) - ord('A')))
        else:
            return False

def add_target_options(parser):
    parser.add_argument("target", help=".py file that contains definition for "
            "the target to test with.")
    parser.add_argument("--sim_cmd",
            help="The command to use to start the actual target (e.g. "
            "simulation)", default="spike")
    parser.add_argument("--server_cmd",
            help="The command to use to start the debug server (e.g. OpenOCD)")

    xlen_group = parser.add_mutually_exclusive_group()
    xlen_group.add_argument("--32", action="store_const", const=32, dest="xlen",
            help="Force the target to be 32-bit.")
    xlen_group.add_argument("--64", action="store_const", const=64, dest="xlen",
            help="Force the target to be 64-bit.")

    parser.add_argument("--isolate", action="store_true",
            help="Try to run in such a way that multiple instances can run at "
            "the same time. This may make it harder to debug a failure if it "
            "does occur.")

def target(parsed):
    directory = os.path.dirname(parsed.target)
    filename = os.path.basename(parsed.target)
    module_name = os.path.splitext(filename)[0]

    sys.path.append(directory)
    module = importlib.import_module(module_name)
    found = []
    for name in dir(module):
        definition = getattr(module, name)
        if type(definition) == type and issubclass(definition, Target):
            found.append(definition)
    assert len(found) == 1, "%s does not define exactly one subclass of " \
            "targets.Target" % parsed.target

    return found[0](parsed.target, parsed)