blob: f48971a7dfb7130c62365c7ecce13dff82542d38 [file] [log] [blame]
# Copyright (c) 2024 Chen Xingyu <hi@xingrz.me>
# SPDX-License-Identifier: Apache-2.0
'''Runner for probe-rs.'''
from runners.core import FileType, RunnerCaps, ZephyrBinaryRunner
DEFAULT_PROBE_RS_GDB_HOST = 'localhost'
DEFAULT_PROBE_RS_GDB_PORT = 1337
class ProbeRsBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for probe-rs.'''
def __init__(self, cfg, chip,
probe_rs='probe-rs',
gdb_host=DEFAULT_PROBE_RS_GDB_HOST,
gdb_port=DEFAULT_PROBE_RS_GDB_PORT,
dev_id=None,
erase=False,
reset=False,
protocol='swd',
speed=None,
connect_under_reset=False,
verify=False,
binary_format='elf',
tool_opt=None):
super().__init__(cfg)
self.probe_rs = probe_rs
self.erase = erase
self.reset = reset
self.protocol = protocol
self.speed = speed
self.connect_under_reset = connect_under_reset
self.verify = verify
self.binary_format = binary_format
self.args = ['--chip', chip]
if dev_id is not None:
self.args += ['--probe', dev_id]
if protocol != 'swd':
self.args += ['--protocol', protocol]
if speed is not None:
self.args += ['--speed', str(speed)]
if connect_under_reset:
self.args += ['--connect-under-reset']
if tool_opt is not None:
self.args += tool_opt
self.file = cfg.file
self.file_type = cfg.file_type
self.elf_name = cfg.elf_file
# GDB configuration (upstream compatible)
self.gdb_cmd = cfg.gdb
self.gdb_host = gdb_host
self.gdb_port = gdb_port
@classmethod
def name(cls):
return 'probe-rs'
@classmethod
def capabilities(cls):
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'},
dev_id=True,
erase=True,
reset=True,
tool_opt=True,
rtt=True,
file=True)
@classmethod
def do_add_parser(cls, parser):
parser.add_argument('--chip', required=True,
help='chip name')
parser.add_argument('--probe-rs', default='probe-rs',
help='path to probe-rs tool, default is probe-rs')
parser.add_argument('--protocol', choices=['swd', 'jtag'], default='swd',
help='debug protocol to use (swd or jtag)')
parser.add_argument('--speed', type=int,
help='protocol speed in kHz')
parser.add_argument('--connect-under-reset', action='store_true',
help='connect under reset')
parser.add_argument('--verify', action='store_true',
help='verify flash after programming')
parser.add_argument('--gdb-host', default=DEFAULT_PROBE_RS_GDB_HOST,
help=f'probe-rs gdb host, defaults to {DEFAULT_PROBE_RS_GDB_HOST}')
parser.add_argument('--gdb-port', type=int, default=DEFAULT_PROBE_RS_GDB_PORT,
help=f'probe-rs gdb port, defaults to {DEFAULT_PROBE_RS_GDB_PORT}')
@classmethod
def dev_id_help(cls) -> str:
return '''select a specific probe, in the form `VID:PID:<Serial>`'''
@classmethod
def tool_opt_help(cls) -> str:
return '''additional options for probe-rs,
e.g. --chip-description-path=/path/to/chip.yml'''
@classmethod
def _get_binary_format_from_args(cls, args):
'''Determine binary format from args.'''
if args.file_type:
# Map FileType to probe-rs format names
format_map = {
FileType.HEX: 'hex',
FileType.BIN: 'bin',
FileType.ELF: 'elf'
}
return format_map.get(args.file_type, 'elf')
elif args.file and args.file.endswith('.hex'):
return 'hex'
else:
return 'elf'
@classmethod
def do_create(cls, cfg, args):
return ProbeRsBinaryRunner(cfg, args.chip,
probe_rs=args.probe_rs,
dev_id=args.dev_id,
erase=args.erase,
reset=args.reset,
protocol=args.protocol,
speed=args.speed,
connect_under_reset=args.connect_under_reset,
verify=args.verify,
gdb_host=args.gdb_host,
gdb_port=args.gdb_port,
binary_format=cls._get_binary_format_from_args(args),
tool_opt=args.tool_opt)
def do_run(self, command, **kwargs):
self.require(self.probe_rs)
if command == 'flash':
self.do_flash(**kwargs)
elif command in ('debug', 'debugserver'):
self.do_debug_debugserver(command, **kwargs)
elif command == 'attach':
self.do_attach(**kwargs)
def do_flash(self, **kwargs):
download_args = []
if self.erase:
download_args += ['--chip-erase']
if self.verify:
download_args += ['--verify']
# Use provided file or default ELF file
flash_file = self.file if self.file else self.elf_name
# Determine format based on file_type or binary_format
if self.file_type:
format_map = {
FileType.HEX: 'hex',
FileType.BIN: 'bin',
FileType.ELF: 'elf'
}
flash_format = format_map.get(self.file_type, 'elf')
else:
flash_format = self.binary_format
download_args += ['--binary-format', flash_format, flash_file]
self.check_call([self.probe_rs, 'download']
+ self.args + download_args)
if self.reset:
self.check_call([self.probe_rs, 'reset']
+ self.args)
def do_debug_debugserver(self, command, **kwargs):
'''Start GDB server or debug session using probe-rs gdb command.'''
debug_args = ['--gdb-connection-string', f"{self.gdb_host}:{self.gdb_port}"]
if command == 'debug':
if self.gdb_cmd is None:
raise ValueError('Cannot debug; gdb is missing')
debug_args += [self.elf_name]
debug_args += ['--gdb', self.gdb_cmd]
else:
# debugserver mode
self.logger.info(f'probe-rs GDB server running on port {self.gdb_port}')
self.check_call([self.probe_rs, 'gdb']
+ self.args + debug_args)
def do_attach(self, **kwargs):
'''Attach to RTT logging using probe-rs attach command.'''
attach_cmd = [self.probe_rs, 'attach'] + self.args + [self.elf_name]
self.logger.info('Starting RTT session')
self.check_call(attach_cmd)