forked from nrfconnect/sdk-zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlinkserver.py
219 lines (173 loc) · 8.32 KB
/
linkserver.py
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# Copyright 2023-2024 NXP
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
#
# Based on jlink.py
'''Runner for debugging with NXP's LinkServer.'''
import logging
import os
import shlex
import subprocess
import sys
from runners.core import ZephyrBinaryRunner, RunnerCaps
DEFAULT_LINKSERVER_EXE = 'Linkserver.exe' if sys.platform == 'win32' else 'LinkServer'
DEFAULT_LINKSERVER_GDB_PORT = 3333
DEFAULT_LINKSERVER_SEMIHOST_PORT = 3334
class LinkServerBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for NXP Linkserver'''
def __init__(self, cfg, device, core,
linkserver=DEFAULT_LINKSERVER_EXE,
dt_flash=True, erase=True,
probe='#1',
gdb_host='',
gdb_port=DEFAULT_LINKSERVER_GDB_PORT,
semihost_port=DEFAULT_LINKSERVER_SEMIHOST_PORT,
override=[],
tui=False, tool_opt=[]):
super().__init__(cfg)
self.file = cfg.file
self.file_type = cfg.file_type
self.hex_name = cfg.hex_file
self.bin_name = cfg.bin_file
self.elf_name = cfg.elf_file
self.gdb_cmd = cfg.gdb if cfg.gdb else None
self.device = device
self.core = core
self.linkserver = linkserver
self.dt_flash = dt_flash
self.erase = erase
self.probe = probe
self.gdb_host = gdb_host
self.gdb_port = gdb_port
self.semihost_port = semihost_port
self.tui_arg = ['-tui'] if tui else []
self.override = override
self.override_cli = self._build_override_cli()
self.tool_opt = []
for opts in [shlex.split(opt) for opt in tool_opt]:
self.tool_opt += opts
@classmethod
def name(cls):
return 'linkserver'
@classmethod
def capabilities(cls):
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'},
dev_id=True, flash_addr=True, erase=True,
tool_opt=True, file=True)
@classmethod
def do_add_parser(cls, parser):
parser.add_argument('--device', required=True, help='device name')
parser.add_argument('--core', required=False, help='core of the device')
parser.add_argument('--probe', default='#1',
help='interface to use (index, or serial number, default is #1')
parser.add_argument('--tui', default=False, action='store_true',
help='if given, GDB uses -tui')
parser.add_argument('--gdb-port', default=DEFAULT_LINKSERVER_GDB_PORT,
help='gdb port to open, defaults to {}'.format(
DEFAULT_LINKSERVER_GDB_PORT))
parser.add_argument('--semihost-port', default=DEFAULT_LINKSERVER_SEMIHOST_PORT,
help='semihost port to open, defaults to the empty string '
'and runs a gdb server')
# keep this, we have to assume that the default 'commander' is on PATH
parser.add_argument('--linkserver', default=DEFAULT_LINKSERVER_EXE,
help=f'''LinkServer executable, default is
{DEFAULT_LINKSERVER_EXE}''')
# user may need to override settings.
parser.add_argument('--override', required=False, action='append',
help=f'''configuration overrides as defined bylinkserver. Example: /device/memory/0/location=0xcafecafe''')
@classmethod
def do_create(cls, cfg, args):
return LinkServerBinaryRunner(cfg, args.device, args.core,
linkserver=args.linkserver,
dt_flash=args.dt_flash,
erase=args.erase,
probe=args.probe,
semihost_port=args.semihost_port,
gdb_port=args.gdb_port,
override=args.override,
tui=args.tui, tool_opt=args.tool_opt)
@property
def linkserver_version_str(self):
if not hasattr(self, '_linkserver_version'):
linkserver_version_cmd=[self.linkserver, "-v"]
ls_output=self.check_output(linkserver_version_cmd)
self.linkserver_version = str(ls_output.split()[1].decode()).lower()
return self.linkserver_version
def do_run(self, command, **kwargs):
self.linkserver = self.require(self.linkserver)
self.logger.info(f'LinkServer: {self.linkserver}, version {self.linkserver_version_str}')
if command == 'flash':
self.flash(**kwargs)
else:
if self.core is not None:
_cmd_core = [ "-c", self.core ]
else:
_cmd_core = []
linkserver_cmd = ([self.linkserver] +
["gdbserver"] +
["--probe", str(self.probe) ] +
["--gdb-port", str(self.gdb_port )] +
["--semihost-port", str(self.semihost_port) ] +
_cmd_core +
self.override_cli +
[self.device])
self.logger.debug(f'LinkServer cmd: + {linkserver_cmd}')
if command in ('debug', 'attach'):
if self.elf_name is None or not os.path.isfile(self.elf_name):
raise ValueError('Cannot debug; elf file required')
gdb_cmd = ([self.gdb_cmd] +
self.tui_arg +
[self.elf_name] +
['-ex', 'target remote {}:{}'.format(self.gdb_host, self.gdb_port)])
if command == 'debug':
gdb_cmd += [ '-ex', 'load', '-ex', 'monitor reset']
if command == 'attach':
linkserver_cmd += ['--attach']
self.run_server_and_client(linkserver_cmd, gdb_cmd)
elif command == 'debugserver':
if self.gdb_host:
raise ValueError('Cannot run debugserver with --gdb-host')
self.check_call(linkserver_cmd)
def do_erase(self, **kwargs):
linkserver_cmd = ([self.linkserver, "flash"] + ["--probe", str(self.probe)] +
[self.device] + ["erase"])
self.logger.debug("flash erase command = " + str(linkserver_cmd))
self.check_call(linkserver_cmd)
def _build_override_cli(self):
override_cli = []
if self.override is not None:
for ov in self.override:
override_cli = (override_cli + ["-o", str(ov)])
return override_cli
def flash(self, **kwargs):
linkserver_cmd = ([self.linkserver, "flash"] + ["--probe", str(self.probe)] + self.override_cli + [self.device])
self.logger.debug(f'LinkServer cmd: + {linkserver_cmd}')
if self.erase:
self.do_erase()
# Use .hex or .bin, preferring .hex over .bin
if self.supports_hex and self.hex_name is not None and os.path.isfile(self.hex_name):
flash_cmd = (["load", self.hex_name])
elif self.bin_name is not None and os.path.isfile(self.bin_name):
if self.dt_flash:
load_addr = self.flash_address_from_build_conf(self.build_conf)
else:
self.logger.critical("no load flash address could be found...")
raise RuntimeError("no load flash address could be found...")
flash_cmd = (["load", "--addr", str(load_addr), self.bin_name])
else:
err = 'Cannot flash; no hex ({}) or bin ({}) file found.'
raise ValueError(err.format(self.hex_name, self.bin_name))
# Flash the selected file
linkserver_cmd = linkserver_cmd + flash_cmd
self.logger.debug("flash command = " + str(linkserver_cmd))
kwargs = {}
if not self.logger.isEnabledFor(logging.DEBUG):
if self.linkserver_version_str < "v1.3.15":
kwargs['stderr'] = subprocess.DEVNULL
else:
kwargs['stdout'] = subprocess.DEVNULL
self.check_call(linkserver_cmd, **kwargs)
def supports_hex(self):
# v1.5.30 has added flash support for Intel Hex files.
return self.linkserver_version_str >= "v1.5.30"