Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions benchkit/helpers/flasher/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Copyright (C) 2026 Vrije Universiteit Brussel. All rights reserved.
# SPDX-License-Identifier: MIT

import pathlib

from benchkit.platforms import Platform

import pathlib
import inspect

class Flasher:
"""
Expand All @@ -15,16 +15,16 @@ class Flasher:
"""

@property
def platform(self) -> Platform:
def platform(self) -> Platform:
"""
Get the platform to run the flasher on
"""
...

def flash(
self,
bin: pathlib.Path,
addr: str,
self,
bin: pathlib.Path,
addr: str,
) -> None:
"""
Flash the binary at the specified address.
Expand All @@ -39,11 +39,13 @@ def reset(self) -> None:
Reset the device.
"""
...

def start(self) -> None:
"""
Start the device (e.g., by running it or exiting reset).
"""
...

def stop(self) -> None:
"""
Stop the device (e.g., by halting it or entering reset).
Expand Down
83 changes: 42 additions & 41 deletions benchkit/helpers/flasher/openocd.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,51 @@
# Copyright (C) 2026 Vrije Universiteit Brussel. All rights reserved.
# SPDX-License-Identifier: MIT

from . import Flasher
import pathlib

from benchkit.platforms import Platform, get_current_platform
from benchkit.helpers.linux.groups import current_user_in_group

import pathlib
from . import Flasher


class OpenOCDFlasher(Flasher):
def __init__(
self,
interface: str,
target: str,
platform: Platform = get_current_platform(),
need_sudo: bool = True,
self,
interface: str,
target: str,
platform: Platform = get_current_platform(),
need_sudo: bool = True,
) -> None:
"""
Args:
interface: openocd interface to use (e.g., "stlink")
target: The OpenOCD target to use (e.g., "stm32l4x").
need_sudo: Whether to use sudo when invoking OpenOCD.
need_sudo: Whether to use sudo when invoking OpenOCD.
"""
self._interface: str | None = f"interface/{interface}.cfg"
self._target: str | None = f"target/{target}.cfg"
self._board: str | None = None
self._platform: Platform = platform # meta class platform property
self._platform: Platform = platform # meta class platform property
self.__need_sudo: bool = need_sudo

@staticmethod
def with_board(
board: str,
need_sudo: bool = True,
board: str,
need_sudo: bool = True,
) -> "OpenOCDFlasher":
"""
Configure the flasher for a specific board. This is a no-op for OpenOCD since the interface and target are already specified.
Configure the flasher for a specific board. This is a no-op for OpenOCD
since the interface and target are already specified.

Args:
board: The name of the board (e.g., "st_nucleo_l4")
need_sudo: Whether to use sudo when invoking OpenOCD
Returns:
An instance of OpenOCDFlasher configured for the specified board.
"""
s = OpenOCDFlasher(
interface = None,
target = None,
interface=None,
target=None,
need_sudo=need_sudo,
)

Expand All @@ -56,34 +58,31 @@ def __cmd_prefix(self) -> str:
Get the command prefix for invoking OpenOCD, optionally with sudo.
"""
return (
f"{'sudo' if self.__need_sudo else ''} openocd -f {self._interface} -f {self._target}"
) if self._board is None else (
f"{'sudo' if self.__need_sudo else ''} openocd -f {self._board}"
(f"{'sudo' if self.__need_sudo else ''} openocd -f {self._interface} -f {self._target}")
if self._board is None
else (f"{'sudo' if self.__need_sudo else ''} openocd -f {self._board}")
)

@property
def platform(self) -> Platform:
return self._platform

def flash(
self,
bin: pathlib.Path,
addr: str,
self,
bin: pathlib.Path,
addr: str,
) -> None:
"""
Flash the firmware onto the board via OpenOCD.
Args:
bin: The path to the binary to flash.
addr: The address to flash the binary to (e.g., "0x08000000").

HACK addr is str because we use the hex format and don't want decimal
"""

self.platform.comm.shell(
command=self.__cmd_prefix.split(" ") + [
"-c",
f"program {bin} {addr} reset exit"
],
command=self.__cmd_prefix.split(" ") + ["-c", f"program {bin} {addr} reset exit"],
print_output=False,
)

Expand All @@ -92,26 +91,24 @@ def reset(self) -> None:
Reset the board via OpenOCD.
"""
self.platform.comm.shell(
command=(
f"{self.__cmd_prefix} "
'-c "init" '
'-c "reset" '
'-c "exit"'
),
command=(f"{self.__cmd_prefix} " '-c "init" ' '-c "reset" ' '-c "exit"'),
print_output=False,
print_input=True,
)


def start(self) -> None:
"""
Start the device (e.g., by running it or exiting reset).
"""
self.platform.comm.shell(
command=self.__cmd_prefix.split(" ") + [
'-c', 'init',
'-c', 'reset run',
'-c', 'exit',
command=self.__cmd_prefix.split(" ")
+ [
"-c",
"init",
"-c",
"reset run",
"-c",
"exit",
],
print_output=False,
print_input=True,
Expand All @@ -122,10 +119,14 @@ def stop(self) -> None:
Stop the device (e.g., by halting it or entering reset).
"""
self.platform.comm.shell(
command=self.__cmd_prefix.split(" ") + [
'-c', 'init',
'-c', 'reset halt',
'-c', 'exit',
command=self.__cmd_prefix.split(" ")
+ [
"-c",
"init",
"-c",
"reset halt",
"-c",
"exit",
],
print_output=False,
print_input=True,
Expand Down
5 changes: 3 additions & 2 deletions tests/test_flasher.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env python3
# Copyright (C) 2026 Vrije Universiteit Brussel. All rights reserved.
# SPDX-License-Identifier: MIT

from benchkit.helpers.flasher.openocd import OpenOCDFlasher
import pathlib

if __name__ == "__main__":
"""
Expand All @@ -12,7 +13,7 @@
device: str = "st_nucleo_l4"
openocd = OpenOCDFlasher.with_board(board=device, need_sudo=True)

# to flash an a file do :
# To flash a file, execute the following:
# openocd.flash(bin=pathlib.Path("firmware.elf").resolve(), addr="0x08000000")

openocd.stop()
Expand Down
Loading