Merge pull request #66 from riscv/debug_user_niceness
[riscv-tests.git] / debug / targets.py
1 import importlib
2 import os.path
3 import sys
4 import tempfile
5
6 import testlib
7
8 class Target(object):
9 # pylint: disable=too-many-instance-attributes
10
11 # Name of the target. Defaults to the name of the class.
12 name = None
13
14 # XLEN of the target. May be overridden with --32 or --64 command line
15 # options.
16 xlen = 0
17
18 # GDB remotetimeout setting.
19 timeout_sec = 2
20
21 # Path to OpenOCD configuration file relative to the .py file where the
22 # target is defined. Defaults to <name>.cfg.
23 openocd_config_path = None
24
25 # Timeout waiting for the server to start up. This is different than the
26 # GDB timeout, which is how long GDB waits for commands to execute.
27 # The server_timeout is how long this script waits for the Server to be ready
28 # for GDB connections.
29 server_timeout_sec = 60
30
31 # Path to linker script relative to the .py file where the target is
32 # defined. Defaults to <name>.lds.
33 link_script_path = None
34
35 # Will be autodetected (by running ExamineTarget) if left unset. Set to
36 # save a little time.
37 misa = None
38
39 # List of commands that should be executed in gdb after connecting but
40 # before starting the test.
41 gdb_setup = []
42
43 # Implements dmode in tdata1 as described in the spec. Targets that need
44 # this value set to False are not compliant with the spec (but still usable
45 # as long as running code doesn't try to mess with triggers set by an
46 # external debugger).
47 honors_tdata1_hmode = True
48
49 # Internal variables:
50 directory = None
51 temporary_files = []
52 temporary_binary = None
53
54 def __init__(self, path, parsed):
55 # Path to module.
56 self.path = path
57 self.directory = os.path.dirname(path)
58 self.server_cmd = parsed.server_cmd
59 self.sim_cmd = parsed.sim_cmd
60 self.isolate = parsed.isolate
61 if not self.name:
62 self.name = type(self).__name__
63 # Default OpenOCD config file to <name>.cfg
64 if not self.openocd_config_path:
65 self.openocd_config_path = "%s.cfg" % self.name
66 self.openocd_config_path = os.path.join(self.directory,
67 self.openocd_config_path)
68 # Default link script to <name>.lds
69 if not self.link_script_path:
70 self.link_script_path = "%s.lds" % self.name
71 self.link_script_path = os.path.join(self.directory,
72 self.link_script_path)
73
74 def create(self):
75 """Create the target out of thin air, eg. start a simulator."""
76 pass
77
78 def server(self):
79 """Start the debug server that gdb connects to, eg. OpenOCD."""
80 return testlib.Openocd(server_cmd=self.server_cmd,
81 config=self.openocd_config_path,
82 timeout=self.server_timeout_sec)
83
84 def compile(self, *sources):
85 binary_name = "%s_%s-%d" % (
86 self.name,
87 os.path.basename(os.path.splitext(sources[0])[0]),
88 self.xlen)
89 if self.isolate:
90 self.temporary_binary = tempfile.NamedTemporaryFile(
91 prefix=binary_name + "_")
92 binary_name = self.temporary_binary.name
93 Target.temporary_files.append(self.temporary_binary)
94 march = "rv%dima" % self.xlen
95 for letter in "fdc":
96 if self.extensionSupported(letter):
97 march += letter
98 testlib.compile(sources +
99 ("programs/entry.S", "programs/init.c",
100 "-I", "../env",
101 "-march=%s" % march,
102 "-T", self.link_script_path,
103 "-nostartfiles",
104 "-mcmodel=medany",
105 "-DXLEN=%d" % self.xlen,
106 "-o", binary_name),
107 xlen=self.xlen)
108 return binary_name
109
110 def extensionSupported(self, letter):
111 # target.misa is set by testlib.ExamineTarget
112 if self.misa:
113 return self.misa & (1 << (ord(letter.upper()) - ord('A')))
114 else:
115 return False
116
117 def add_target_options(parser):
118 parser.add_argument("target", help=".py file that contains definition for "
119 "the target to test with.")
120 parser.add_argument("--sim_cmd",
121 help="The command to use to start the actual target (e.g. "
122 "simulation)", default="spike")
123 parser.add_argument("--server_cmd",
124 help="The command to use to start the debug server (e.g. OpenOCD)")
125
126 xlen_group = parser.add_mutually_exclusive_group()
127 xlen_group.add_argument("--32", action="store_const", const=32, dest="xlen",
128 help="Force the target to be 32-bit.")
129 xlen_group.add_argument("--64", action="store_const", const=64, dest="xlen",
130 help="Force the target to be 64-bit.")
131
132 parser.add_argument("--isolate", action="store_true",
133 help="Try to run in such a way that multiple instances can run at "
134 "the same time. This may make it harder to debug a failure if it "
135 "does occur.")
136
137 def target(parsed):
138 directory = os.path.dirname(parsed.target)
139 filename = os.path.basename(parsed.target)
140 module_name = os.path.splitext(filename)[0]
141
142 sys.path.append(directory)
143 module = importlib.import_module(module_name)
144 found = []
145 for name in dir(module):
146 definition = getattr(module, name)
147 if type(definition) == type and issubclass(definition, Target):
148 found.append(definition)
149 assert len(found) == 1, "%s does not define exactly one subclass of " \
150 "targets.Target" % parsed.target
151
152 return found[0](parsed.target, parsed)