146 lines
5.7 KiB
Python
146 lines
5.7 KiB
Python
# 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: 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
|