diff --git a/boards/common/trace32.board.cmake b/boards/common/trace32.board.cmake new file mode 100644 index 00000000000..7f66b2590ea --- /dev/null +++ b/boards/common/trace32.board.cmake @@ -0,0 +1,6 @@ +# Copyright 2022 NXP +# SPDX-License-Identifier: Apache-2.0 + +board_set_flasher_ifnset(trace32) +board_set_debugger_ifnset(trace32) +board_finalize_runner_args(trace32) diff --git a/doc/develop/flash_debug/host-tools.rst b/doc/develop/flash_debug/host-tools.rst index eea6e0a9948..5493b0b30e7 100644 --- a/doc/develop/flash_debug/host-tools.rst +++ b/doc/develop/flash_debug/host-tools.rst @@ -255,6 +255,58 @@ These debug host tools are compatible with the following debug probes: Check if your SoC is listed in `pyOCD Supported Devices`_. +.. _lauterbach-trace32-debug-host-tools: + +Lauterbach TRACE32 Debug Host Tools +*********************************** + +`Lauterbach TRACE32`_ is a product line of microprocessor development tools, +debuggers and real-time tracer with support for JTAG, SWD, NEXUS or ETM over +multiple core architectures, including Arm Cortex-A/-R/-M, RISC-V, Xtensa, etc. +Zephyr allows users to develop and program boards with Lauterbach TRACE32 +support using :ref:`west `. + +The runner consists of a wrapper around TRACE32 software, and allows a Zephyr +board to execute a custom start-up script (Practice Script) for the different +commands supported, including the ability to pass extra arguments from CMake. +Is up to the board using this runner to define the actions performed on each +command. + +Install Lauterbach TRACE32 Software +----------------------------------- + +Download Lauterbach TRACE32 software from the `Lauterbach TRACE32 download website`_ +(registration required) and follow the installation steps described in +`Lauterbach TRACE32 Installation Guide`_. + +Flashing and Debugging +---------------------- + +Set the :ref:`environment variable ` :envvar:`T32_DIR` to the TRACE32 +system directory. Then execute ``west flash`` or ``west debug`` commands to +flash or debug the Zephyr application as detailed in :ref:`west-build-flash-debug`. +The ``debug`` command launches TRACE32 GUI to allow debug the Zephyr +application, while the ``flash`` command hides the GUI and perform all +operations in the background. + +By default, the ``t32`` runner will launch TRACE32 using the default +configuration file named ``config.t32`` located in the TRACE32 system +directory. To use a different configuration file, supply the argument +``--config CONFIG`` to the runner, for example: + +.. code-block:: console + + west flash --config myconfig.t32 + +For more options, run ``west flash --context -r t32`` to print the usage. + +Zephyr RTOS Awareness +--------------------- + +To enable Zephyr RTOS awareness follow the steps described in +`Lauterbach TRACE32 Zephyr OS Awareness Manual`_. + + .. _J-Link Software and Documentation Pack: https://www.segger.com/downloads/jlink/#J-LinkSoftwareAndDocumentationPack @@ -272,3 +324,15 @@ Check if your SoC is listed in `pyOCD Supported Devices`_. .. _OpenOCD Windows: http://gnutoolchains.com/arm-eabi/openocd/ + +.. _Lauterbach TRACE32: + https://www.lauterbach.com/ + +.. _Lauterbach TRACE32 download website: + http://www.lauterbach.com/download_trace32.html + +.. _Lauterbach TRACE32 Installation Guide: + https://www2.lauterbach.com/pdf/installation.pdf + +.. _Lauterbach TRACE32 Zephyr OS Awareness Manual: + https://www2.lauterbach.com/pdf/rtos_zephyr.pdf diff --git a/scripts/west_commands/runners/__init__.py b/scripts/west_commands/runners/__init__.py index b2d7ba328aa..90eefcadffe 100644 --- a/scripts/west_commands/runners/__init__.py +++ b/scripts/west_commands/runners/__init__.py @@ -48,6 +48,7 @@ _names = [ 'spi_burn', 'stm32cubeprogrammer', 'stm32flash', + 'trace32', 'xtensa', # Keep this list sorted by runner name; don't add to the end. ] diff --git a/scripts/west_commands/runners/trace32.py b/scripts/west_commands/runners/trace32.py new file mode 100644 index 00000000000..3b7dd35bf56 --- /dev/null +++ b/scripts/west_commands/runners/trace32.py @@ -0,0 +1,145 @@ +# Copyright 2022 NXP +# SPDX-License-Identifier: Apache-2.0 + +'''Runner for Lauterbach TRACE32.''' + +import argparse +import os +import platform +import subprocess +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import List, Optional + +from runners.core import BuildConfiguration, RunnerCaps, RunnerConfig, ZephyrBinaryRunner + +DEFAULT_T32_CONFIG = Path('config.t32') + + +class TRACE32BinaryRunner(ZephyrBinaryRunner): + ''' + Runner front-end for Lauterbach TRACE32. + + The runner is a wrapper around Lauterbach TRACE32 PowerView. It executes a Lauterbach Practice + script (.cmm) after launching the debugger, which should be located at + zephyr/boards//support/.cmm, where is the board directory and + is the name of the west runner command executed (e.g. flash or debug). Extra arguments can be + passed to the startup script by using the command line option --startup-args. + ''' + + def __init__(self, + cfg: RunnerConfig, + t32_cfg: Path, + arch: str, + startup_args: List[str] = None, + timeout: int = 60) -> None: + super(TRACE32BinaryRunner, self).__init__(cfg) + self.arch = arch + self.t32_cfg = t32_cfg + self.t32_exec: Optional[Path] = None + self.startup_dir = Path(cfg.board_dir) / 'support' + self.startup_args = startup_args + self.timeout = timeout + + @classmethod + def name(cls) -> str: + return 'trace32' + + @classmethod + def capabilities(cls) -> RunnerCaps: + return RunnerCaps(commands={'flash', 'debug'}) + + @classmethod + def do_add_parser(cls, parser: argparse.ArgumentParser) -> None: + parser.add_argument('--arch', + default='auto', + choices=('auto', 'arm', 'riscv', 'xtensa'), + help='Target architecture. Set to "auto" to select the architecture ' + 'based on CONFIG_ARCH value') + parser.add_argument('--config', + default=DEFAULT_T32_CONFIG, + type=Path, + help='Override TRACE32 configuration file path. Can be a relative path ' + 'to T32_DIR environment variable, or an absolute path') + parser.add_argument('--startup-args', + nargs='*', + help='Arguments to pass to the start-up script') + parser.add_argument('--timeout', + default=60, + type=int, + help='Timeout, in seconds, of the flash operation') + + @classmethod + def do_create(cls, cfg: RunnerConfig, args: argparse.Namespace) -> 'TRACE32BinaryRunner': + build_conf = BuildConfiguration(cfg.build_dir) + if args.arch == 'auto': + arch = build_conf.get('CONFIG_ARCH').replace('"', '') + # there is a single binary for all ARM architectures + arch = arch.replace('arm64', 'arm') + else: + arch = args.arch + return TRACE32BinaryRunner(cfg, args.config, arch, startup_args=args.startup_args, + timeout=args.timeout) + + def do_run(self, command, **kwargs) -> None: + t32_dir = os.environ.get('T32_DIR') + if not t32_dir: + raise RuntimeError('T32_DIR environment variable undefined') + + if platform.system() == 'Windows': + os_name = 'windows64' + suffix = '.exe' + elif platform.system() == 'Linux': + os_name = 'pc_linux64' + suffix = '' + else: + raise RuntimeError('Host OS not supported by this runner') + + self.t32_exec = Path(t32_dir) / 'bin' / os_name / f't32m{self.arch}{suffix}' + if not self.t32_exec.exists(): + raise RuntimeError(f'Cannot find Lauterbach executable at {self.t32_exec}') + + if not self.t32_cfg.is_absolute(): + self.t32_cfg = Path(t32_dir) / self.t32_cfg + if not self.t32_cfg.exists(): + raise RuntimeError(f'Cannot find Lauterbach configuration at {self.t32_cfg}') + + startup_script = self.startup_dir / f'{command}.cmm' + if not startup_script.exists(): + raise RuntimeError(f'Cannot find start-up script at {startup_script}') + + if command == 'flash': + self.flash(**kwargs) + elif command == 'debug': + self.debug(**kwargs) + + def flash(self, **kwargs) -> None: + with TemporaryDirectory(suffix='t32') as tmp_dir: + # use a temporary config file, based on the provided configuration, + # to hide the TRACE32 software graphical interface + cfg_content = f'{self.t32_cfg.read_text()}\n\nSCREEN=OFF\n' + tmp_cfg = Path(tmp_dir) / DEFAULT_T32_CONFIG.name + tmp_cfg.write_text(cfg_content) + + cmd = self.get_launch_command('flash', cfg=tmp_cfg) + self.logger.info(f'Launching TRACE32: {" ".join(cmd)}') + try: + self.check_call(cmd, timeout=self.timeout) + self.logger.info('Finished') + except subprocess.TimeoutExpired: + self.logger.error(f'Timed out after {self.timeout} seconds') + + def debug(self, **kwargs) -> None: + cmd = self.get_launch_command('debug') + self.logger.info(f'Launching TRACE32: {" ".join(cmd)}') + self.check_call(cmd) + + def get_launch_command(self, command_name: str, cfg: Path = None) -> List[str]: + cmd = [ + str(self.t32_exec), + '-c', str(cfg if cfg else self.t32_cfg), + '-s', str(self.startup_dir / f'{command_name}.cmm') + ] + if self.startup_args: + cmd.extend(self.startup_args) + return cmd diff --git a/scripts/west_commands/tests/test_imports.py b/scripts/west_commands/tests/test_imports.py index 7c07afc4d76..d0804be8c8c 100644 --- a/scripts/west_commands/tests/test_imports.py +++ b/scripts/west_commands/tests/test_imports.py @@ -38,5 +38,6 @@ def test_runner_imports(): 'spi_burn', 'stm32cubeprogrammer', 'stm32flash', + 'trace32', 'xtensa')) assert runner_names == expected