west: runners: uf2: Add new UF2 runner.
Add a new UF2 runner, supporting only the flash capability. Searches for FAT partitions containing `INFO_UF2.TXT` files, and can optionally filter on a matching `Board-ID` value in that file. Signed-off-by: Peter Johanson <peter@peterjohanson.com>
This commit is contained in:
parent
b2c75f423a
commit
26a04adb8b
4
boards/common/uf2.board.cmake
Normal file
4
boards/common/uf2.board.cmake
Normal file
|
@ -0,0 +1,4 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
board_set_flasher_ifnset(uf2)
|
||||
board_finalize_runner_args(uf2) # No default arguments to provide.
|
|
@ -48,6 +48,10 @@ function(runners_yaml_append_config)
|
|||
get_runners_prop(bin_file bin "${KERNEL_BIN_NAME}")
|
||||
runners_yaml_append(" bin_file: ${bin}")
|
||||
endif()
|
||||
if(CONFIG_BUILD_OUTPUT_UF2)
|
||||
get_runners_prop(uf2_file uf2 "${KERNEL_UF2_NAME}")
|
||||
runners_yaml_append(" uf2_file: ${uf2}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_GDB OR OPENOCD OR OPENOCD_DEFAULT_PATH)
|
||||
runners_yaml_append(" # Host tools:")
|
||||
|
|
|
@ -413,6 +413,7 @@ def get_runner_config(build_dir, yaml_path, runners_yaml, args=None):
|
|||
output_file('elf'),
|
||||
output_file('hex'),
|
||||
output_file('bin'),
|
||||
output_file('uf2'),
|
||||
config('file'),
|
||||
filetype('file_type'),
|
||||
config('gdb'),
|
||||
|
|
|
@ -49,6 +49,7 @@ _names = [
|
|||
'stm32cubeprogrammer',
|
||||
'stm32flash',
|
||||
'trace32',
|
||||
'uf2',
|
||||
'xtensa',
|
||||
# Keep this list sorted by runner name; don't add to the end.
|
||||
]
|
||||
|
|
|
@ -285,6 +285,7 @@ class RunnerConfig(NamedTuple):
|
|||
elf_file: Optional[str] # zephyr.elf path, or None
|
||||
hex_file: Optional[str] # zephyr.hex path, or None
|
||||
bin_file: Optional[str] # zephyr.bin path, or None
|
||||
uf2_file: Optional[str] # zephyr.uf2 path, or None
|
||||
file: Optional[str] # binary file path (provided by the user), or None
|
||||
file_type: Optional[FileType] = FileType.OTHER # binary file type
|
||||
gdb: Optional[str] = None # path to a usable gdb
|
||||
|
@ -758,7 +759,7 @@ class ZephyrBinaryRunner(abc.ABC):
|
|||
else:
|
||||
return
|
||||
|
||||
if output_type in ('elf', 'hex', 'bin'):
|
||||
if output_type in ('elf', 'hex', 'bin', 'uf2'):
|
||||
err += f' Try enabling CONFIG_BUILD_OUTPUT_{output_type.upper()}.'
|
||||
|
||||
# RuntimeError avoids a stack trace saved in run_common.
|
||||
|
|
105
scripts/west_commands/runners/uf2.py
Normal file
105
scripts/west_commands/runners/uf2.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
# Copyright (c) 2023 Peter Johanson <peter@peterjohanson.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'''UF2 runner (flash only) for UF2 compatible bootloaders.'''
|
||||
|
||||
from pathlib import Path
|
||||
from shutil import copy
|
||||
|
||||
from runners.core import ZephyrBinaryRunner, RunnerCaps
|
||||
|
||||
try:
|
||||
import psutil # pylint: disable=unused-import
|
||||
MISSING_PSUTIL = False
|
||||
except ImportError:
|
||||
# This can happen when building the documentation for the
|
||||
# runners package if psutil is not on sys.path. This is fine
|
||||
# to ignore in that case.
|
||||
MISSING_PSUTIL = True
|
||||
|
||||
class UF2BinaryRunner(ZephyrBinaryRunner):
|
||||
'''Runner front-end for copying to UF2 USB-MSC mounts.'''
|
||||
|
||||
def __init__(self, cfg, board_id=None):
|
||||
super().__init__(cfg)
|
||||
self.board_id = board_id
|
||||
|
||||
@classmethod
|
||||
def name(cls):
|
||||
return 'uf2'
|
||||
|
||||
@classmethod
|
||||
def capabilities(cls):
|
||||
return RunnerCaps(commands={'flash'})
|
||||
|
||||
@classmethod
|
||||
def do_add_parser(cls, parser):
|
||||
parser.add_argument('--board-id', dest='board_id',
|
||||
help='Board-ID value to match from INFO_UF2.TXT')
|
||||
|
||||
@classmethod
|
||||
def do_create(cls, cfg, args):
|
||||
return UF2BinaryRunner(cfg, board_id=args.board_id)
|
||||
|
||||
@staticmethod
|
||||
def get_uf2_info_path(part) -> Path:
|
||||
return Path(part.mountpoint) / "INFO_UF2.TXT"
|
||||
|
||||
@staticmethod
|
||||
def is_uf2_partition(part):
|
||||
try:
|
||||
return ((part.fstype in ['vfat', 'FAT']) and
|
||||
UF2BinaryRunner.get_uf2_info_path(part).is_file())
|
||||
except PermissionError:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_uf2_info(part):
|
||||
lines = UF2BinaryRunner.get_uf2_info_path(part).read_text().splitlines()
|
||||
|
||||
lines = lines[1:] # Skip the first summary line
|
||||
|
||||
def split_uf2_info(line: str):
|
||||
k, _, val = line.partition(':')
|
||||
return k.strip(), val.strip()
|
||||
|
||||
return {k: v for k, v in (split_uf2_info(line) for line in lines) if k and v}
|
||||
|
||||
def match_board_id(self, part):
|
||||
info = self.get_uf2_info(part)
|
||||
|
||||
return info.get('Board-ID') == self.board_id
|
||||
|
||||
def get_uf2_partitions(self):
|
||||
parts = [part for part in psutil.disk_partitions() if self.is_uf2_partition(part)]
|
||||
|
||||
if (self.board_id is not None) and parts:
|
||||
parts = [part for part in parts if self.match_board_id(part)]
|
||||
if not parts:
|
||||
self.logger.warning("Discovered UF2 partitions don't match Board-ID '%s'",
|
||||
self.board_id)
|
||||
|
||||
return parts
|
||||
|
||||
def copy_uf2_to_partition(self, part):
|
||||
self.ensure_output('uf2')
|
||||
|
||||
copy(self.cfg.uf2_file, part.mountpoint)
|
||||
|
||||
def do_run(self, command, **kwargs):
|
||||
if MISSING_PSUTIL:
|
||||
raise RuntimeError(
|
||||
'could not import psutil; something may be wrong with the '
|
||||
'python environment')
|
||||
|
||||
partitions = self.get_uf2_partitions()
|
||||
if not partitions:
|
||||
raise RuntimeError('No matching UF2 partitions found')
|
||||
|
||||
if len(partitions) > 1:
|
||||
raise RuntimeError('More than one matching UF2 partitions found')
|
||||
|
||||
part = partitions[0]
|
||||
self.logger.info("Copying UF2 file to '%s'", part.mountpoint)
|
||||
self.copy_uf2_to_partition(part)
|
|
@ -39,5 +39,6 @@ def test_runner_imports():
|
|||
'stm32cubeprogrammer',
|
||||
'stm32flash',
|
||||
'trace32',
|
||||
'uf2',
|
||||
'xtensa'))
|
||||
assert runner_names == expected
|
||||
|
|
Loading…
Reference in a new issue