mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-14 10:15:21 +00:00
I've had some requests to be able to use code in the runners package without having west installed. It turns out to be pretty easy to make this happen, as west is currently only used for west.log and some trivial helper methods: - To replace west log, use the standard logging module - Add an appropriate handler for each runner's logger in run_common.py which delegates to west.log, to keep output working as expected. Signed-off-by: Marti Bolivar <marti.bolivar@nordicsemi.no>
183 lines
7.1 KiB
Python
183 lines
7.1 KiB
Python
# Copyright (c) 2017 Linaro Limited.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
'''Runner for debugging with J-Link.'''
|
|
|
|
import argparse
|
|
import os
|
|
import shlex
|
|
import sys
|
|
import tempfile
|
|
|
|
from runners.core import ZephyrBinaryRunner, RunnerCaps, \
|
|
BuildConfiguration
|
|
|
|
DEFAULT_JLINK_EXE = 'JLink.exe' if sys.platform == 'win32' else 'JLinkExe'
|
|
DEFAULT_JLINK_GDB_PORT = 2331
|
|
|
|
class ToggleAction(argparse.Action):
|
|
|
|
def __call__(self, parser, args, ignored, option):
|
|
setattr(args, self.dest, not option.startswith('--no-'))
|
|
|
|
class JLinkBinaryRunner(ZephyrBinaryRunner):
|
|
'''Runner front-end for the J-Link GDB server.'''
|
|
|
|
def __init__(self, cfg, device,
|
|
commander=DEFAULT_JLINK_EXE,
|
|
flash_addr=0x0, erase=True, reset_after_load=False,
|
|
iface='swd', speed='auto',
|
|
gdbserver='JLinkGDBServer', gdb_port=DEFAULT_JLINK_GDB_PORT,
|
|
tui=False, tool_opt=[]):
|
|
super(JLinkBinaryRunner, self).__init__(cfg)
|
|
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.commander = commander
|
|
self.flash_addr = flash_addr
|
|
self.erase = erase
|
|
self.reset_after_load = reset_after_load
|
|
self.gdbserver = gdbserver
|
|
self.iface = iface
|
|
self.speed = speed
|
|
self.gdb_port = gdb_port
|
|
self.tui_arg = ['-tui'] if tui else []
|
|
|
|
self.tool_opt = []
|
|
for opts in [shlex.split(opt) for opt in tool_opt]:
|
|
self.tool_opt += opts
|
|
|
|
@classmethod
|
|
def name(cls):
|
|
return 'jlink'
|
|
|
|
@classmethod
|
|
def capabilities(cls):
|
|
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'},
|
|
flash_addr=True)
|
|
|
|
@classmethod
|
|
def do_add_parser(cls, parser):
|
|
# Required:
|
|
parser.add_argument('--device', required=True, help='device name')
|
|
|
|
# Optional:
|
|
parser.add_argument('--iface', default='swd',
|
|
help='interface to use, default is swd')
|
|
parser.add_argument('--speed', default='auto',
|
|
help='interface speed, default is autodetect')
|
|
parser.add_argument('--tui', default=False, action='store_true',
|
|
help='if given, GDB uses -tui')
|
|
parser.add_argument('--gdbserver', default='JLinkGDBServer',
|
|
help='GDB server, default is JLinkGDBServer')
|
|
parser.add_argument('--gdb-port', default=DEFAULT_JLINK_GDB_PORT,
|
|
help='pyocd gdb port, defaults to {}'.format(
|
|
DEFAULT_JLINK_GDB_PORT))
|
|
parser.add_argument('--tool-opt', default=[], action='append',
|
|
help='''Additional options for JLink Commander,
|
|
e.g. \'-autoconnect 1\' ''')
|
|
parser.add_argument('--commander', default=DEFAULT_JLINK_EXE,
|
|
help='J-Link Commander, default is JLinkExe')
|
|
parser.add_argument('--erase', default=False, action='store_true',
|
|
help='if given, mass erase flash before loading')
|
|
parser.add_argument('--reset-after-load', '--no-reset-after-load',
|
|
dest='reset_after_load', nargs=0,
|
|
action=ToggleAction,
|
|
help='reset after loading? (default: no)')
|
|
|
|
parser.set_defaults(reset_after_load=False)
|
|
|
|
@classmethod
|
|
def create(cls, cfg, args):
|
|
build_conf = BuildConfiguration(cfg.build_dir)
|
|
flash_addr = cls.get_flash_address(args, build_conf)
|
|
return JLinkBinaryRunner(cfg, args.device,
|
|
commander=args.commander,
|
|
flash_addr=flash_addr, erase=args.erase,
|
|
reset_after_load=args.reset_after_load,
|
|
iface=args.iface, speed=args.speed,
|
|
gdbserver=args.gdbserver,
|
|
gdb_port=args.gdb_port,
|
|
tui=args.tui, tool_opt=args.tool_opt)
|
|
|
|
def print_gdbserver_message(self):
|
|
self.logger.info('J-Link GDB server running on port {}'.
|
|
format(self.gdb_port))
|
|
|
|
def do_run(self, command, **kwargs):
|
|
server_cmd = ([self.gdbserver] +
|
|
['-select', 'usb', # only USB connections supported
|
|
'-port', str(self.gdb_port),
|
|
'-if', self.iface,
|
|
'-speed', self.speed,
|
|
'-device', self.device,
|
|
'-silent',
|
|
'-singlerun'] +
|
|
self.tool_opt)
|
|
|
|
if command == 'flash':
|
|
self.flash(**kwargs)
|
|
elif command == 'debugserver':
|
|
self.require(self.gdbserver)
|
|
self.print_gdbserver_message()
|
|
self.check_call(server_cmd)
|
|
else:
|
|
self.require(self.gdbserver)
|
|
if self.gdb_cmd is None:
|
|
raise ValueError('Cannot debug; gdb is missing')
|
|
if self.elf_name is None:
|
|
raise ValueError('Cannot debug; elf is missing')
|
|
client_cmd = (self.gdb_cmd +
|
|
self.tui_arg +
|
|
[self.elf_name] +
|
|
['-ex', 'target remote :{}'.format(self.gdb_port)])
|
|
if command == 'debug':
|
|
client_cmd += ['-ex', 'monitor halt',
|
|
'-ex', 'monitor reset',
|
|
'-ex', 'load']
|
|
if self.reset_after_load:
|
|
client_cmd += ['-ex', 'monitor reset']
|
|
|
|
self.print_gdbserver_message()
|
|
self.run_server_and_client(server_cmd, client_cmd)
|
|
|
|
def flash(self, **kwargs):
|
|
self.require(self.commander)
|
|
if self.bin_name is None:
|
|
raise ValueError('Cannot flash; bin_name is missing')
|
|
|
|
lines = ['r'] # Reset and halt the target
|
|
|
|
if self.erase:
|
|
lines.append('erase') # Erase all flash sectors
|
|
|
|
lines.append('loadfile {} 0x{:x}'.format(self.bin_name,
|
|
self.flash_addr))
|
|
if self.reset_after_load:
|
|
lines.append('r') # Reset and halt the target
|
|
|
|
lines.append('g') # Start the CPU
|
|
lines.append('q') # Close the connection and quit
|
|
|
|
self.logger.debug('JLink commander script:')
|
|
self.logger.debug('\n'.join(lines))
|
|
|
|
# Don't use NamedTemporaryFile: the resulting file can't be
|
|
# opened again on Windows.
|
|
with tempfile.TemporaryDirectory(suffix='jlink') as d:
|
|
fname = os.path.join(d, 'runner.jlink')
|
|
with open(fname, 'wb') as f:
|
|
f.writelines(bytes(line + '\n', 'utf-8') for line in lines)
|
|
|
|
cmd = ([self.commander] +
|
|
['-if', self.iface,
|
|
'-speed', self.speed,
|
|
'-device', self.device,
|
|
'-CommanderScript', fname] +
|
|
self.tool_opt)
|
|
|
|
self.logger.info('Flashing Target Device')
|
|
self.check_call(cmd)
|