Browse Source

scripts: west_commands: runners: add ST-LINK GDB server

Add the ST-Link GDB server (part of the STM32CubeCLT) as west runner.

The STM32CubeCLT is an all-in-one multi-OS command-line toolset, which is
part of the STM32Cube ecosystem, and notably includes a GDB server for
debugging using ST-Link probes.

This runner supports the "attach", "debug" and "debugserver" commands.

Documentation: https://www.st.com/en/development-tools/stm32cubeclt.html
Signed-off-by: Mathieu Choplain <mathieu.choplain@st.com>
pull/91613/merge
Mathieu Choplain 3 weeks ago committed by Fabio Baltieri
parent
commit
19fe604aef
  1. 4
      boards/common/stlink_gdbserver.board.cmake
  2. 50
      doc/develop/flash_debug/host-tools.rst
  3. 45
      doc/develop/flash_debug/probes.rst
  4. 1
      scripts/west_commands/runners/__init__.py
  5. 188
      scripts/west_commands/runners/stlink_gdbserver.py
  6. 1
      scripts/west_commands/tests/test_imports.py

4
boards/common/stlink_gdbserver.board.cmake

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
# SPDX-License-Identifier: Apache-2.0
board_set_debugger_ifnset(stlink_gdbserver)
board_finalize_runner_args(stlink_gdbserver)

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

@ -525,6 +525,50 @@ to ``rfp-cli`` when flashing: @@ -525,6 +525,50 @@ to ``rfp-cli`` when flashing:
west flash --rfp-cli ~/Downloads/RFP_CLI_Linux_V31800_x64/linux-x64/rfp-cli
.. _stm32cubeclt-host-tools:
.. _runner_stlink_gdbserver:
STM32CubeCLT Flash & Debug Host Tools
*************************************
STMicroelectronics provides `STM32CubeCLT`_ as an official all-in-one toolset compatible with
Linux |reg|, macOS |reg| and Windows |reg|, allowing the use of STMicroelectronics proprietary
tools within third-party development environments.
It notably provides a GDB debugging server (the *ST-LINK GDB Server*) that can be used to debug
applications on STM32 boards thanks to on-board or external ST-LINK debug probes.
It is compatible with the following debug probes:
- :ref:`stlink-v21-onboard-debug-probe`
- Standalone `ST-LINK-V2`_, `ST-LINK-V3`_, and `STLINK-V3PWR`_ probes
Install STM32CubeCLT
--------------------
The easiest way to get the ST-LINK GDB Server is to install `STM32CubeCLT`_ from STMicroelectronics' website.
A valid email address is needed to receive the downloading link.
Basic usage
-----------
The ST-Link GDB Server can be used through the ``west attach``, ``west debug`` or ``west debugserver`` commands
to debug Zephyr applications.
.. code-block:: console
west debug --runner stlink_gdbserver
.. note::
The `STM32CubeProgrammer`_ version contained in the `STM32CubeCLT`_ installation can also be used to flash
applications. To do so, the dedicated :ref:`STM32CubeProgrammer runner <runner_stm32cubeprogrammer>` should
be used instead of ``stlink_gdbserver``, as done in the following example:
.. code-block:: console
west flash --runner stm32cubeprogrammer
.. _stm32cubeprog-flash-host-tools:
.. _runner_stm32cubeprogrammer:
@ -650,12 +694,12 @@ For more about the UF2 format and its tooling, see `USB Flashing Format (UF2)`_. @@ -650,12 +694,12 @@ For more about the UF2 format and its tooling, see `USB Flashing Format (UF2)`_.
.. _probe-rs Supported Devices:
https://probe.rs/targets/
.. _STM32CubeProgrammer:
https://www.st.com/en/development-tools/stm32cubeprog.html
.. _STM32CubeCLT:
https://www.st.com/en/development-tools/stm32cubeclt.html
.. _STM32CubeProgrammer:
https://www.st.com/en/development-tools/stm32cubeprog.html
.. _STM32CubeProgrammer User Manual:
https://www.st.com/resource/en/user_manual/um2237-stm32cubeprogrammer-software-description-stmicroelectronics.pdf

45
doc/develop/flash_debug/probes.rst

@ -36,29 +36,29 @@ with pyOCD or OpenOCD debug host tools, or with J-Link firmware to communicate @@ -36,29 +36,29 @@ with pyOCD or OpenOCD debug host tools, or with J-Link firmware to communicate
with J-Link debug host tools.
+------------------------------------------+---------------------------------------------------------------------------------------------------------+
+------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------+
|| *Debug Probes & Host Tools* | Host Tools |
+| *Compatibility Chart* +--------------------+--------------------+---------------------+--------------------+--------------------+
| | **J-Link Debug** | **OpenOCD** | **pyOCD** | **NXP S32DS** | **NXP LinkServer** |
+----------------+-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
| | **J-Link External** | ✓ | ✓ | | | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
| | **LPC-Link2 CMSIS-DAP** | | | | | ✓ |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
| | **LPC-Link2 J-Link** | ✓ | | | | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
| | **MCU-Link CMSIS-DAP** | | | | | ✓ |
| Debug Probes +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
| | **MCU-Link J-Link** | ✓ | | | | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
| | **NXP S32 Debug Probe** | | | | ✓ | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
| | **OpenSDA DAPLink** | | ✓ | ✓ | | ✓ |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
| | **OpenSDA J-Link** | ✓ | | | | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
| | **ST-LINK/V2-1** | ✓ | ✓ | *some STM32 boards* | | |
+----------------+-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+
+| *Compatibility Chart* +--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **J-Link Debug** | **OpenOCD** | **pyOCD** | **NXP S32DS** | **NXP LinkServer** | **ST-LINK GDB Server** |
+----------------+-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **J-Link External** | ✓ | ✓ | | | | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **LPC-Link2 CMSIS-DAP** | | | | | ✓ | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **LPC-Link2 J-Link** | ✓ | | | | | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **MCU-Link CMSIS-DAP** | | | | | ✓ | |
| Debug Probes +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **MCU-Link J-Link** | ✓ | | | | | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **NXP S32 Debug Probe** | | | | ✓ | | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **OpenSDA DAPLink** | | ✓ | ✓ | | ✓ | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **OpenSDA J-Link** | ✓ | | | | | |
| +-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
| | **ST-LINK/V2-1** | ✓ | ✓ | *some STM32 boards* | | | ✓ |
+----------------+-------------------------+--------------------+--------------------+---------------------+--------------------+--------------------+------------------------+
Some supported boards in Zephyr do not include an onboard debug probe and
@ -374,6 +374,7 @@ It is compatible with the following host debug tools: @@ -374,6 +374,7 @@ It is compatible with the following host debug tools:
- :ref:`openocd-debug-host-tools`
- :ref:`jlink-debug-host-tools`
- :ref:`stm32cubeclt-host-tools`
For some STM32 based boards, it is also compatible with:

1
scripts/west_commands/runners/__init__.py

@ -59,6 +59,7 @@ _names = [ @@ -59,6 +59,7 @@ _names = [
'silabs_commander',
'spi_burn',
'spsdk',
'stlink_gdbserver',
'stm32cubeprogrammer',
'stm32flash',
'sy1xx',

188
scripts/west_commands/runners/stlink_gdbserver.py

@ -0,0 +1,188 @@ @@ -0,0 +1,188 @@
# Copyright (c) 2025 STMicroelectronics
#
# SPDX-License-Identifier: Apache-2.0
"""
Runner for debugging applications using the ST-LINK GDB server
from STMicroelectronics, provided as part of the STM32CubeCLT.
"""
import argparse
import platform
import re
import shutil
from pathlib import Path
from runners.core import MissingProgram, RunnerCaps, RunnerConfig, ZephyrBinaryRunner
STLINK_GDB_SERVER_DEFAULT_PORT = 61234
class STLinkGDBServerRunner(ZephyrBinaryRunner):
@classmethod
def _get_stm32cubeclt_paths(cls) -> tuple[Path, Path]:
"""
Returns a tuple of two elements of class pathlib.Path:
[0]: path to the ST-LINK_gdbserver executable
[1]: path to the "STM32CubeProgrammer/bin" folder
"""
def find_highest_clt_version(tools_folder: Path) -> Path | None:
if not tools_folder.is_dir():
return None
# List all CubeCLT installations present in tools folder
CUBECLT_FLDR_RE = re.compile(r"stm32cubeclt_([1-9]).(\d+).(\d+)", re.IGNORECASE)
installations: list[tuple[int, Path]] = []
for f in tools_folder.iterdir():
m = CUBECLT_FLDR_RE.match(f.name)
if m is not None:
# Compute a number that can be easily compared
# from the STM32CubeCLT version number
major, minor, revis = int(m[1]), int(m[2]), int(m[3])
ver_num = major * 1000000 + minor * 1000 + revis
installations.append((ver_num, f))
if len(installations) == 0:
return None
# Sort candidates and return the path to the most recent version
most_recent_install = sorted(installations, key=lambda e: e[0], reverse=True)[0]
return most_recent_install[1]
cur_platform = platform.system()
# Attempt to find via shutil.which()
if cur_platform in ["Linux", "Windows"]:
gdbserv = shutil.which("ST-LINK_gdbserver")
cubeprg = shutil.which("STM32_Programmer_CLI")
if gdbserv and cubeprg:
# Return the parent of cubeprg as [1] should be the path
# to the folder containing STM32_Programmer_CLI, not the
# path to the executable itself
return (Path(gdbserv), Path(cubeprg).parent)
# Search in OS-specific paths
search_path: str
tool_suffix = ""
if cur_platform == "Linux":
search_path = "/opt/st/"
elif cur_platform == "Windows":
search_path = "C:\\ST\\"
tool_suffix = ".exe"
elif cur_platform == "Darwin":
search_path = "/opt/ST/"
else:
raise RuntimeError("Unsupported OS")
clt = find_highest_clt_version(Path(search_path))
if clt is None:
raise MissingProgram("ST-LINK_gdbserver (from STM32CubeCLT)")
gdbserver_path = clt / "STLink-gdb-server" / "bin" / f"ST-LINK_gdbserver{tool_suffix}"
cubeprg_bin_path = clt / "STM32CubeProgrammer" / "bin"
return (gdbserver_path, cubeprg_bin_path)
@classmethod
def name(cls) -> str:
return "stlink_gdbserver"
@classmethod
def capabilities(cls) -> RunnerCaps:
return RunnerCaps(commands={"attach", "debug", "debugserver"}, dev_id=True, extload=True)
@classmethod
def extload_help(cls) -> str:
return "External Loader for ST-Link GDB server"
@classmethod
def do_add_parser(cls, parser: argparse.ArgumentParser):
# Expose a subset of the ST-LINK GDB server arguments
parser.add_argument(
"--swd", action='store_true', default=True, help="Enable SWD debug mode"
)
parser.add_argument("--apid", type=int, default=0, help="Target DAP ID")
parser.add_argument(
"--port-number",
type=int,
default=STLINK_GDB_SERVER_DEFAULT_PORT,
help="Port number for GDB client",
)
@classmethod
def do_create(cls, cfg: RunnerConfig, args: argparse.Namespace) -> "STLinkGDBServerRunner":
return STLinkGDBServerRunner(
cfg, args.swd, args.apid, args.dev_id, args.port_number, args.extload
)
def __init__(
self,
cfg: RunnerConfig,
swd: bool,
ap_id: int | None,
stlink_serial: str | None,
gdb_port: int,
external_loader: str | None,
):
super().__init__(cfg)
self.ensure_output('elf')
self._swd = swd
self._gdb_port = gdb_port
self._stlink_serial = stlink_serial
self._ap_id = ap_id
self._external_loader = external_loader
def do_run(self, command: str, **kwargs):
if command in ["attach", "debug", "debugserver"]:
self.do_attach_debug_debugserver(command)
else:
raise ValueError(f"{command} not supported")
def do_attach_debug_debugserver(self, command: str):
# self.ensure_output('elf') is called in constructor
# and validated that self.cfg.elf_file is non-null.
# This assertion is required for the test framework,
# which doesn't have this insight - it should never
# trigger in real-world scenarios.
assert self.cfg.elf_file is not None
elf_path = Path(self.cfg.elf_file).as_posix()
gdb_args = ["-ex", f"target remote :{self._gdb_port}", elf_path]
(gdbserver_path, cubeprg_path) = STLinkGDBServerRunner._get_stm32cubeclt_paths()
gdbserver_cmd = [gdbserver_path.as_posix()]
gdbserver_cmd += ["--stm32cubeprogrammer-path", str(cubeprg_path.absolute())]
gdbserver_cmd += ["--port-number", str(self._gdb_port)]
gdbserver_cmd += ["--apid", str(self._ap_id)]
gdbserver_cmd += ["--halt"]
if self._swd:
gdbserver_cmd.append("--swd")
if command == "attach":
gdbserver_cmd += ["--attach"]
else: # debug/debugserver
gdbserver_cmd += ["--initialize-reset"]
gdb_args += ["-ex", f"load {elf_path}"]
if self._stlink_serial:
gdbserver_cmd += ["--serial-number", self._stlink_serial]
if self._external_loader:
extldr_path = cubeprg_path / "ExternalLoader" / self._external_loader
if not extldr_path.exists():
raise RuntimeError(f"External loader {self._external_loader} does not exist")
gdbserver_cmd += ["--extload", str(extldr_path)]
self.require(gdbserver_cmd[0])
if command == "debugserver":
self.check_call(gdbserver_cmd)
elif self.cfg.gdb is None: # attach/debug
raise RuntimeError("GDB is required for attach/debug")
else: # attach/debug
gdb_cmd = [self.cfg.gdb] + gdb_args
self.require(gdb_cmd[0])
self.run_server_and_client(gdbserver_cmd, gdb_cmd)

1
scripts/west_commands/tests/test_imports.py

@ -50,6 +50,7 @@ def test_runner_imports(): @@ -50,6 +50,7 @@ def test_runner_imports():
'silabs_commander',
'spi_burn',
'spsdk',
'stlink_gdbserver',
'stm32cubeprogrammer',
'stm32flash',
'sy1xx',

Loading…
Cancel
Save