runners: add support for stm32cubeprogrammer

Add support for the official ST Microelectronics programming tool (CLI
version).

Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
This commit is contained in:
Gerard Marull-Paretas 2020-10-10 21:20:55 +02:00 committed by Maureen Helm
parent fabf40398f
commit f98dd24993
4 changed files with 205 additions and 0 deletions

View file

@ -0,0 +1,4 @@
# SPDX-License-Identifier: Apache-2.0
board_set_flasher_ifnset(stm32cubeprogrammer)
board_finalize_runner_args(stm32cubeprogrammer)

View file

@ -29,6 +29,7 @@ from runners import qemu
from runners import stm32flash from runners import stm32flash
from runners import xtensa from runners import xtensa
from runners import mdb from runners import mdb
from runners import stm32cubeprogrammer
def get_runner_cls(runner): def get_runner_cls(runner):
'''Get a runner's class object, given its name.''' '''Get a runner's class object, given its name.'''

View file

@ -0,0 +1,199 @@
# 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
from pathlib import Path
import platform
import os
import shlex
from typing import List, Optional, ClassVar, Dict
from runners.core import ZephyrBinaryRunner, RunnerCaps, RunnerConfig
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: Optional[int],
reset_mode: Optional[str],
conn_modifiers: Optional[str],
cli: Optional[Path],
use_elf: bool,
erase: bool,
tool_opt: List[str],
) -> None:
super().__init__(cfg)
self._port = port
self._frequency = frequency
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
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":
return (
Path.home()
/ "STMicroelectronics"
/ "STM32Cube"
/ "STM32CubeProgrammer"
/ "bin"
/ "STM32_Programmer_CLI"
)
if platform.system() == "Windows":
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["PROGRAMFILES"]) / 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)
@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(
"--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",
)
parser.add_argument(
"--tool-opt",
default=[],
action="append",
help="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,
conn_modifiers=args.conn_modifiers,
cli=args.cli,
use_elf=args.use_elf,
erase=args.erase,
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
# 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
self.check_call(cmd + ["--download", dl_file, "--start"])

View file

@ -31,6 +31,7 @@ def test_runner_imports():
'openocd', 'openocd',
'pyocd', 'pyocd',
'qemu', 'qemu',
'stm32cubeprogrammer',
'stm32flash', 'stm32flash',
'xtensa')) 'xtensa'))
assert runner_names == expected assert runner_names == expected