Browse Source

west: runner: add support for NXP's linkserver

Linkserver is a utility for launching and managing GDB servers for NXP
debug probes, which also provides a command-line target flash programming
capabilities. Linkserver can be used with NXP MCUXpresso for Visual Studio
Code.

For more information about LinkServer, please visit the LinkServer web
page (link [1] below).

This commit adds a runner to west, supporting debug and flash commands.

Documentation is also added.

[1] - LinkServer web page:
    https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/linkserver-for-microcontrollers:LINKERSERVER

Signed-off-by: Yves Vandervennet <yves.vandervennet@nxp.com>
pull/60711/head
Yves Vandervennet 2 years ago committed by Mahesh Mahadevan
parent
commit
58e4df6460
  1. 4
      boards/common/linkserver.board.cmake
  2. 49
      doc/develop/flash_debug/host-tools.rst
  3. 29
      doc/develop/flash_debug/probes.rst
  4. 1
      scripts/west_commands/runners/__init__.py
  5. 196
      scripts/west_commands/runners/linkserver.py
  6. 1
      scripts/west_commands/tests/test_imports.py

4
boards/common/linkserver.board.cmake

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
# Copyright 2023 NXP
# SPDX-License-Identifier: Apache-2.0
board_finalize_runner_args(linkserver "--dt-flash=y")

49
doc/develop/flash_debug/host-tools.rst

@ -199,6 +199,55 @@ Windows PATH. A specific bossac executable can be used by passing the @@ -199,6 +199,55 @@ Windows PATH. A specific bossac executable can be used by passing the
WSL is not currently supported.
.. _linkserver-debug-host-tools:
LinkServer Debug Host Tools
****************************
Linkserver is a utility for launching and managing GDB servers for NXP debug probes,
which also provides a command-line target flash programming capabilities.
Linkserver can be used with NXP MCUXpresso for Visual Studio Code implementation,
with custom debug configurations based on GNU tools or as part of a headless solution
for continuous integration and test. Linkserver can be used with MCU-Link, LPC-Link2,
LPC11U35-based and OpenSDA based standalone or on-board debug probes from NXP.
The Linkserver installer also includes the firmware update utilities for MCU-Link and
the LPCScrypt utility for use with LPC-Link2. Linkserver can also be installed using
the MCUXpresso Installer.
LinkServer is compatible with the following debug probes:
- :ref:`lpclink2-cmsis-onboard-debug-probe`
Supported west commands:
1. flash
#. debug
#. debugserver
#. attach
Notes:
1. Probes can be listed with LinkServer:
.. code-block:: console
LinkServer probes
2. Use the LinkServer west runner ``--probe`` option to pass the probe index.
.. code-block:: console
west flash --runner=linkserver --probe=3
3. device specific settings can be overridden with the west runner for LinkServer with
the option '--override'. May be used multiple times. The format is dictated
by LinkServer, e.g.:
.. code-block:: console
west flash --runner=linkserver --override /device/memory/5/flash-driver=MIMXRT500_SFDP_MXIC_OSPI_S.cfx
.. _jlink-debug-host-tools:
J-Link Debug Host Tools

29
doc/develop/flash_debug/probes.rst

@ -58,6 +58,35 @@ onboard debug probe may have limitations, such as lack of support for advanced @@ -58,6 +58,35 @@ onboard debug probe may have limitations, such as lack of support for advanced
debuggers or high-speed tracing. You may need to adjust jumpers to prevent the
onboard debug probe from interfering with the external debug probe.
.. _lpclink2-cmsis-onboard-debug-probe:
LPC-LINK2 CMSIS DAP Onboard Debug Probe
***************************************
The CMSIS-DAP debug probes allow debugging from any compatible toolchain,
including IAR EWARM, Keil MDK, as well as NXP’s MCUXpresso IDE and
MCUXpresso extension for VS Code.
As well as providing debug probe functionality, the LPC-Link2 probes also
provide:
1. SWO trace end point: this virtual device is used by MCUXpresso to retrieve
SWO trace data. See the MCUXpresso IDE documentation for more information.
2. Virtual COM (VCOM) port / UART bridge connected to the target processor
3. LPCSIO bridge that provides communication to I2C and SPI slave devices
This probe is realized by programming the LPC-Link2 microcontroller with the CMSIS-DAP
LPC-Link2 firmware. Download and install `LPCScrypt`_ to get the firmware and
programming scripts.
.. note:: Verify the firmware supports your board by visiting `Firmware for LPCXpresso`_
1. Put the LPC-Link2 microcontroller into DFU boot mode by attaching the DFU
jumper, then powering up the board.
#. Run the ``program_CMSIS`` script.
#. Remove the DFU jumper and power cycle the board.
.. _lpclink2-jlink-onboard-debug-probe:
LPC-Link2 J-Link Onboard Debug Probe

1
scripts/west_commands/runners/__init__.py

@ -37,6 +37,7 @@ _names = [ @@ -37,6 +37,7 @@ _names = [
'intel_adsp',
'intel_cyclonev',
'jlink',
'linkserver',
'mdb',
'misc',
'native_gdb',

196
scripts/west_commands/runners/linkserver.py

@ -0,0 +1,196 @@ @@ -0,0 +1,196 @@
# Copyright 2023 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,
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.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('--probe', default=1,
help='interface to use (index, no 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,
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())
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:
linkserver_cmd = ([self.linkserver] +
["gdbserver"] +
["--probe", "#"+str(self.probe) ] +
["--gdb-port", str(self.gdb_port )] +
["--semihost-port", str(self.semihost_port) ] +
self.override_cli +
[self.device])
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])
if self.erase:
self.do_erase()
if 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 bin ({}) file found.'
raise ValueError(err.format(self.bin_name))
# Flash the selected elf file
linkserver_cmd = linkserver_cmd + flash_cmd
self.logger.debug("flash command = " + str(linkserver_cmd))
kwargs = {}
if not self.logger.isEnabledFor(logging.DEBUG):
kwargs['stderr'] = subprocess.DEVNULL
self.check_call(linkserver_cmd, **kwargs)

1
scripts/west_commands/tests/test_imports.py

@ -27,6 +27,7 @@ def test_runner_imports(): @@ -27,6 +27,7 @@ def test_runner_imports():
'intel_adsp',
'intel_cyclonev',
'jlink',
'linkserver',
'mdb-nsim',
'mdb-hw',
'misc-flasher',

Loading…
Cancel
Save