Add new '--start-address' argument to the STM32CubeProgrammer west runner. This argument can be used to specify an address from which the MCU should start executing, instead of relying on the default value in CubeProgrammer. Also update STM32CubeProgrammer runner tests to support new argument. Signed-off-by: Mathieu Choplain <mathieu.choplain@st.com>
251 lines
7.8 KiB
Python
251 lines
7.8 KiB
Python
# Copyright (c) 2020 Teslabs Engineering S.L.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""Runner for flashing with STM32CubeProgrammer CLI, the official programming
|
|
utility from ST Microelectronics.
|
|
"""
|
|
|
|
import argparse
|
|
import functools
|
|
import os
|
|
import platform
|
|
import shlex
|
|
import shutil
|
|
from pathlib import Path
|
|
from typing import ClassVar
|
|
|
|
from runners.core import RunnerCaps, RunnerConfig, ZephyrBinaryRunner
|
|
|
|
|
|
class STM32CubeProgrammerBinaryRunner(ZephyrBinaryRunner):
|
|
"""Runner front-end for STM32CubeProgrammer CLI."""
|
|
|
|
_RESET_MODES: ClassVar[dict[str, str]] = {
|
|
"sw": "SWrst",
|
|
"hw": "HWrst",
|
|
"core": "Crst",
|
|
}
|
|
"""Reset mode argument mappings."""
|
|
|
|
def __init__(
|
|
self,
|
|
cfg: RunnerConfig,
|
|
port: str,
|
|
frequency: int | None,
|
|
reset_mode: str | None,
|
|
start_address: int | None,
|
|
conn_modifiers: str | None,
|
|
cli: Path | None,
|
|
use_elf: bool,
|
|
erase: bool,
|
|
extload: str | None,
|
|
tool_opt: list[str],
|
|
) -> None:
|
|
super().__init__(cfg)
|
|
|
|
self._port = port
|
|
self._frequency = frequency
|
|
self._start_address = start_address
|
|
self._reset_mode = reset_mode
|
|
self._conn_modifiers = conn_modifiers
|
|
self._cli = (
|
|
cli or STM32CubeProgrammerBinaryRunner._get_stm32cubeprogrammer_path()
|
|
)
|
|
self._use_elf = use_elf
|
|
self._erase = erase
|
|
|
|
if extload:
|
|
p = (
|
|
STM32CubeProgrammerBinaryRunner._get_stm32cubeprogrammer_path().parent.resolve()
|
|
/ 'ExternalLoader'
|
|
)
|
|
self._extload = ['-el', str(p / extload)]
|
|
else:
|
|
self._extload = []
|
|
|
|
self._tool_opt: list[str] = list()
|
|
for opts in [shlex.split(opt) for opt in tool_opt]:
|
|
self._tool_opt += opts
|
|
|
|
# add required library loader path to the environment (Linux only)
|
|
if platform.system() == "Linux":
|
|
os.environ["LD_LIBRARY_PATH"] = str(self._cli.parent / ".." / "lib")
|
|
|
|
@staticmethod
|
|
def _get_stm32cubeprogrammer_path() -> Path:
|
|
"""Obtain path of the STM32CubeProgrammer CLI tool."""
|
|
|
|
if platform.system() == "Linux":
|
|
cmd = shutil.which("STM32_Programmer_CLI")
|
|
if cmd is not None:
|
|
return Path(cmd)
|
|
|
|
return (
|
|
Path.home()
|
|
/ "STMicroelectronics"
|
|
/ "STM32Cube"
|
|
/ "STM32CubeProgrammer"
|
|
/ "bin"
|
|
/ "STM32_Programmer_CLI"
|
|
)
|
|
|
|
if platform.system() == "Windows":
|
|
cmd = shutil.which("STM32_Programmer_CLI")
|
|
if cmd is not None:
|
|
return Path(cmd)
|
|
|
|
cli = (
|
|
Path("STMicroelectronics")
|
|
/ "STM32Cube"
|
|
/ "STM32CubeProgrammer"
|
|
/ "bin"
|
|
/ "STM32_Programmer_CLI.exe"
|
|
)
|
|
x86_path = Path(os.environ["PROGRAMFILES(X86)"]) / cli
|
|
if x86_path.exists():
|
|
return x86_path
|
|
|
|
return Path(os.environ["PROGRAMW6432"]) / cli
|
|
|
|
if platform.system() == "Darwin":
|
|
return (
|
|
Path("/Applications")
|
|
/ "STMicroelectronics"
|
|
/ "STM32Cube"
|
|
/ "STM32CubeProgrammer"
|
|
/ "STM32CubeProgrammer.app"
|
|
/ "Contents"
|
|
/ "MacOs"
|
|
/ "bin"
|
|
/ "STM32_Programmer_CLI"
|
|
)
|
|
|
|
raise NotImplementedError("Could not determine STM32_Programmer_CLI path")
|
|
|
|
@classmethod
|
|
def name(cls):
|
|
return "stm32cubeprogrammer"
|
|
|
|
@classmethod
|
|
def capabilities(cls):
|
|
return RunnerCaps(commands={"flash"}, erase=True, extload=True, tool_opt=True)
|
|
|
|
@classmethod
|
|
def do_add_parser(cls, parser):
|
|
parser.add_argument(
|
|
"--port",
|
|
type=str,
|
|
required=True,
|
|
help="Interface identifier, e.g. swd, jtag, /dev/ttyS0...",
|
|
)
|
|
parser.add_argument(
|
|
"--frequency", type=int, required=False, help="Programmer frequency in KHz"
|
|
)
|
|
parser.add_argument(
|
|
"--reset-mode",
|
|
type=str,
|
|
required=False,
|
|
choices=["sw", "hw", "core"],
|
|
help="Reset mode",
|
|
)
|
|
parser.add_argument(
|
|
"--start-address",
|
|
# To accept arguments in hex format, a wrapper lambda around int() must be used.
|
|
# Wrapping the lambda with functools.wraps() makes it so that 'invalid int value'
|
|
# is displayed when an invalid value is provided for this argument.
|
|
type=functools.wraps(int)(lambda s: int(s, base=0)),
|
|
required=False,
|
|
help="Address where execution should begin after flashing"
|
|
)
|
|
parser.add_argument(
|
|
"--conn-modifiers",
|
|
type=str,
|
|
required=False,
|
|
help="Additional options for the --connect argument",
|
|
)
|
|
parser.add_argument(
|
|
"--cli",
|
|
type=Path,
|
|
required=False,
|
|
help="STM32CubeProgrammer CLI tool path",
|
|
)
|
|
parser.add_argument(
|
|
"--use-elf",
|
|
action="store_true",
|
|
required=False,
|
|
help="Use ELF file when flashing instead of HEX file",
|
|
)
|
|
|
|
@classmethod
|
|
def extload_help(cls) -> str:
|
|
return "External Loader for STM32_Programmer_CLI"
|
|
|
|
@classmethod
|
|
def tool_opt_help(cls) -> str:
|
|
return "Additional options for STM32_Programmer_CLI"
|
|
|
|
@classmethod
|
|
def do_create(
|
|
cls, cfg: RunnerConfig, args: argparse.Namespace
|
|
) -> "STM32CubeProgrammerBinaryRunner":
|
|
return STM32CubeProgrammerBinaryRunner(
|
|
cfg,
|
|
port=args.port,
|
|
frequency=args.frequency,
|
|
reset_mode=args.reset_mode,
|
|
start_address=args.start_address,
|
|
conn_modifiers=args.conn_modifiers,
|
|
cli=args.cli,
|
|
use_elf=args.use_elf,
|
|
erase=args.erase,
|
|
extload=args.extload,
|
|
tool_opt=args.tool_opt,
|
|
)
|
|
|
|
def do_run(self, command: str, **kwargs):
|
|
if command == "flash":
|
|
self.flash(**kwargs)
|
|
|
|
def flash(self, **kwargs) -> None:
|
|
self.require(str(self._cli))
|
|
|
|
# prepare base command
|
|
cmd = [str(self._cli)]
|
|
|
|
connect_opts = f"port={self._port}"
|
|
if self._frequency:
|
|
connect_opts += f" freq={self._frequency}"
|
|
if self._reset_mode:
|
|
reset_mode = STM32CubeProgrammerBinaryRunner._RESET_MODES[self._reset_mode]
|
|
connect_opts += f" reset={reset_mode}"
|
|
if self._conn_modifiers:
|
|
connect_opts += f" {self._conn_modifiers}"
|
|
|
|
cmd += ["--connect", connect_opts]
|
|
cmd += self._tool_opt
|
|
if self._extload:
|
|
# external loader to come after the tool option in STM32CubeProgrammer
|
|
cmd += self._extload
|
|
|
|
# erase first if requested
|
|
if self._erase:
|
|
self.check_call(cmd + ["--erase", "all"])
|
|
|
|
# flash image and run application
|
|
dl_file = self.cfg.elf_file if self._use_elf else self.cfg.hex_file
|
|
if dl_file is None:
|
|
raise RuntimeError('cannot flash; no download file was specified')
|
|
elif not os.path.isfile(dl_file):
|
|
raise RuntimeError(f'download file {dl_file} does not exist')
|
|
|
|
flash_and_run_args = ["--download", dl_file]
|
|
|
|
# '--start' is needed to start execution after flash.
|
|
# The default start address is the beggining of the flash,
|
|
# but another value can be explicitly specified if desired.
|
|
flash_and_run_args.append("--start")
|
|
if self._start_address is not None:
|
|
flash_and_run_args.append(f"0x{self._start_address:X}")
|
|
|
|
self.check_call(cmd + flash_and_run_args)
|