Skip to content

Commit b0163db

Browse files
committed
west: runners: probe-rs: Add enhanced functionality
Add comprehensive enhancements to the probe-rs runner: - New parameters: --reset, --protocol, --speed, --verify, etc - RTT logging support via 'attach' command - Smart binary format detection with FileType support - Enhanced debug/debugserver with improved GDB integration - Conditional reset after flashing instead of always resetting Maintains upstream compatibility while significantly expanding probe-rs capabilities for embedded development workflows. Signed-off-by: Jared Wolff <hello@jaredwolff.com>
1 parent 2a88cb5 commit b0163db

File tree

1 file changed

+95
-8
lines changed

1 file changed

+95
-8
lines changed

scripts/west_commands/runners/probe_rs.py

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
'''Runner for probe-rs.'''
55

6-
from runners.core import RunnerCaps, ZephyrBinaryRunner
6+
from runners.core import FileType, RunnerCaps, ZephyrBinaryRunner
77

88
DEFAULT_PROBE_RS_GDB_HOST = 'localhost'
99
DEFAULT_PROBE_RS_GDB_PORT = 1337
@@ -17,22 +17,46 @@ def __init__(self, cfg, chip,
1717
gdb_port=DEFAULT_PROBE_RS_GDB_PORT,
1818
dev_id=None,
1919
erase=False,
20+
reset=False,
21+
protocol='swd',
22+
speed=None,
23+
connect_under_reset=False,
24+
verify=False,
25+
binary_format='elf',
2026
tool_opt=None):
2127
super().__init__(cfg)
2228

2329
self.probe_rs = probe_rs
2430
self.erase = erase
31+
self.reset = reset
32+
self.protocol = protocol
33+
self.speed = speed
34+
self.connect_under_reset = connect_under_reset
35+
self.verify = verify
36+
self.binary_format = binary_format
2537

2638
self.args = ['--chip', chip]
2739

2840
if dev_id is not None:
2941
self.args += ['--probe', dev_id]
3042

43+
if protocol != 'swd':
44+
self.args += ['--protocol', protocol]
45+
46+
if speed is not None:
47+
self.args += ['--speed', str(speed)]
48+
49+
if connect_under_reset:
50+
self.args += ['--connect-under-reset']
51+
3152
if tool_opt is not None:
3253
self.args += tool_opt
3354

55+
self.file = cfg.file
56+
self.file_type = cfg.file_type
3457
self.elf_name = cfg.elf_file
3558

59+
# GDB configuration (upstream compatible)
3660
self.gdb_cmd = cfg.gdb
3761
self.gdb_host = gdb_host
3862
self.gdb_port = gdb_port
@@ -43,23 +67,33 @@ def name(cls):
4367

4468
@classmethod
4569
def capabilities(cls):
46-
return RunnerCaps(commands={'flash', 'debug', 'debugserver'},
70+
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'},
4771
dev_id=True,
4872
erase=True,
49-
tool_opt=True)
73+
reset=True,
74+
tool_opt=True,
75+
rtt=True,
76+
file=True)
5077

5178
@classmethod
5279
def do_add_parser(cls, parser):
5380
parser.add_argument('--chip', required=True,
5481
help='chip name')
5582
parser.add_argument('--probe-rs', default='probe-rs',
5683
help='path to probe-rs tool, default is probe-rs')
84+
parser.add_argument('--protocol', choices=['swd', 'jtag'], default='swd',
85+
help='debug protocol to use (swd or jtag)')
86+
parser.add_argument('--speed', type=int,
87+
help='protocol speed in kHz')
88+
parser.add_argument('--connect-under-reset', action='store_true',
89+
help='connect under reset')
90+
parser.add_argument('--verify', action='store_true',
91+
help='verify flash after programming')
5792
parser.add_argument('--gdb-host', default=DEFAULT_PROBE_RS_GDB_HOST,
5893
help=f'probe-rs gdb host, defaults to {DEFAULT_PROBE_RS_GDB_HOST}')
59-
parser.add_argument('--gdb-port', default=DEFAULT_PROBE_RS_GDB_PORT,
94+
parser.add_argument('--gdb-port', type=int, default=DEFAULT_PROBE_RS_GDB_PORT,
6095
help=f'probe-rs gdb port, defaults to {DEFAULT_PROBE_RS_GDB_PORT}')
6196

62-
6397
@classmethod
6498
def dev_id_help(cls) -> str:
6599
return '''select a specific probe, in the form `VID:PID:<Serial>`'''
@@ -69,12 +103,36 @@ def tool_opt_help(cls) -> str:
69103
return '''additional options for probe-rs,
70104
e.g. --chip-description-path=/path/to/chip.yml'''
71105

106+
@classmethod
107+
def _get_binary_format_from_args(cls, args):
108+
'''Determine binary format from args.'''
109+
if args.file_type:
110+
# Map FileType to probe-rs format names
111+
format_map = {
112+
FileType.HEX: 'hex',
113+
FileType.BIN: 'bin',
114+
FileType.ELF: 'elf'
115+
}
116+
return format_map.get(args.file_type, 'elf')
117+
elif args.file and args.file.endswith('.hex'):
118+
return 'hex'
119+
else:
120+
return 'elf'
121+
72122
@classmethod
73123
def do_create(cls, cfg, args):
74124
return ProbeRsBinaryRunner(cfg, args.chip,
75125
probe_rs=args.probe_rs,
76126
dev_id=args.dev_id,
77127
erase=args.erase,
128+
reset=args.reset,
129+
protocol=args.protocol,
130+
speed=args.speed,
131+
connect_under_reset=args.connect_under_reset,
132+
verify=args.verify,
133+
gdb_host=args.gdb_host,
134+
gdb_port=args.gdb_port,
135+
binary_format=cls._get_binary_format_from_args(args),
78136
tool_opt=args.tool_opt)
79137

80138
def do_run(self, command, **kwargs):
@@ -83,24 +141,53 @@ def do_run(self, command, **kwargs):
83141
self.do_flash(**kwargs)
84142
elif command in ('debug', 'debugserver'):
85143
self.do_debug_debugserver(command, **kwargs)
144+
elif command == 'attach':
145+
self.do_attach(**kwargs)
86146

87147
def do_flash(self, **kwargs):
88148
download_args = []
89149
if self.erase:
90150
download_args += ['--chip-erase']
91-
download_args += [self.elf_name]
151+
if self.verify:
152+
download_args += ['--verify']
153+
# Use provided file or default ELF file
154+
flash_file = self.file if self.file else self.elf_name
155+
# Determine format based on file_type or binary_format
156+
if self.file_type:
157+
format_map = {
158+
FileType.HEX: 'hex',
159+
FileType.BIN: 'bin',
160+
FileType.ELF: 'elf'
161+
}
162+
flash_format = format_map.get(self.file_type, 'elf')
163+
else:
164+
flash_format = self.binary_format
165+
download_args += ['--binary-format', flash_format, flash_file]
92166

93167
self.check_call([self.probe_rs, 'download']
94168
+ self.args + download_args)
95169

96-
self.check_call([self.probe_rs, 'reset']
97-
+ self.args)
170+
if self.reset:
171+
self.check_call([self.probe_rs, 'reset']
172+
+ self.args)
98173

99174
def do_debug_debugserver(self, command, **kwargs):
175+
'''Start GDB server or debug session using probe-rs gdb command.'''
100176
debug_args = ['--gdb-connection-string', f"{self.gdb_host}:{self.gdb_port}"]
101177
if command == 'debug':
178+
if self.gdb_cmd is None:
179+
raise ValueError('Cannot debug; gdb is missing')
102180
debug_args += [self.elf_name]
103181
debug_args += ['--gdb', self.gdb_cmd]
182+
else:
183+
# debugserver mode
184+
self.logger.info(f'probe-rs GDB server running on port {self.gdb_port}')
104185

105186
self.check_call([self.probe_rs, 'gdb']
106187
+ self.args + debug_args)
188+
189+
def do_attach(self, **kwargs):
190+
'''Attach to RTT logging using probe-rs attach command.'''
191+
attach_cmd = [self.probe_rs, 'attach'] + self.args + [self.elf_name]
192+
self.logger.info('Starting RTT session')
193+
self.check_call(attach_cmd)

0 commit comments

Comments
 (0)