You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
5.8 KiB
146 lines
5.8 KiB
# 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/<board>/support/<command>.cmm, where <board> is the board directory and <command> |
|
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: Optional[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: Optional[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
|
|
|