west: runners: rfp: Add Reneses Flash Programmer runner

Add a basic RFP runner that supports the `flash` command targettting
the Renesas Standard Boot Firmware.

Signed-off-by: Peter Johanson <peter@peterjohanson.com>
This commit is contained in:
Peter Johanson 2025-03-27 14:55:01 -06:00 committed by Anas Nashif
commit 4399d9e3a2
6 changed files with 405 additions and 0 deletions

View file

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

View file

@ -484,6 +484,25 @@ Check if your SoC is listed in `probe-rs Supported Devices`_.
.. _stm32cubeprog-flash-host-tools:
Renesas Flash Programmer (RFP) Host Tools
*****************************************
Renesas provides `Renesas Flash Programmer`_ as an official programming tool for Renesas boards
using the Renesas standard boot firmware. It is available as a GUI and CLI.
For boards configured with the ``rfp`` west runner, the RFP CLI can be easily used to flash Zephyr.
Supported west commands:
1. flash
Once downloaded, if ``rfp-cli`` is not placed somewhere in your system PATH, you can pass the location
to ``rfp-cli`` when flashing:
.. code-block:: console
west flash --rfp-cli ~/Downloads/RFP_CLI_Linux_V31800_x64/linux-x64/rfp-cli
STM32CubeProgrammer Flash Host Tools
************************************
@ -572,6 +591,9 @@ For advanced usage via the GUI or CLI, check out the `STM32CubeProgrammer User M
.. _NXP S32 Design Studio for S32 Platform:
https://www.nxp.com/design/software/development-software/s32-design-studio-ide/s32-design-studio-for-s32-platform:S32DS-S32PLATFORM
.. _Renesas Flash Programmer:
https://www.renesas.com/en/software-tool/renesas-flash-programmer-programming-gui
.. _S32 Design Studio for S32 Platform Installation User Guide:
https://www.nxp.com/webapp/Download?colCode=S32DSIG

View file

@ -55,6 +55,7 @@ _names = [
'qemu',
'renode',
'renode-robot',
'rfp',
'silabs_commander',
'spi_burn',
'stm32cubeprogrammer',

View file

@ -0,0 +1,120 @@
# Copyright (c) 2025 Pete Johanson
#
# SPDX-License-Identifier: Apache-2.0
#
# pylint: disable=duplicate-code
'''Runner for rfp.'''
import platform
import re
from runners.core import RunnerCaps, ZephyrBinaryRunner
if platform.system() == 'Darwin':
DEFAULT_RFP_PORT = None
else:
DEFAULT_RFP_PORT = '/dev/ttyACM0'
def to_num(number):
dev_match = re.search(r"^\d*\+dev", number)
dev_version = dev_match is not None
num_match = re.search(r"^\d*", number)
num = int(num_match.group(0))
if dev_version:
num += 1
return num
class RfpBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for rfp.'''
def __init__(
self,
cfg,
rfp_cli='rfp-cli',
device=None,
erase=False,
verify=False,
port=DEFAULT_RFP_PORT,
speed=None,
):
super().__init__(cfg)
self.rfp_cmd = [rfp_cli]
self.verify = verify
self.erase = erase
self.port = port
self.device = device
self.speed = speed
@classmethod
def name(cls):
return 'rfp'
@classmethod
def capabilities(cls):
return RunnerCaps(commands={'flash'}, erase=True)
@classmethod
def do_add_parser(cls, parser):
parser.add_argument(
'--rfp-cli', default='rfp-cli', help='path to rfp-cli, default is rfp-cli'
)
parser.add_argument(
'--port',
default=DEFAULT_RFP_PORT,
help='serial port to use, default is ' + str(DEFAULT_RFP_PORT),
)
parser.add_argument('--device', help='Specify the device type to pass to rfp-cli')
parser.add_argument('--verify', action='store_true', help='if given, verify after flash')
parser.add_argument('--speed', help='Specify the serial port speed')
@classmethod
def do_create(cls, cfg, args):
return RfpBinaryRunner(
cfg,
rfp_cli=args.rfp_cli,
device=args.device,
port=args.port,
erase=args.erase,
speed=args.speed,
verify=args.verify,
)
def do_run(self, command, **kwargs):
if command == 'flash':
self.do_flash(**kwargs)
else:
self.logger.error("Unsuppported command")
def do_flash(self, **kwargs):
self.ensure_output('hex')
hex_name = self.cfg.hex_file
self.logger.info(f'Flashing file: {hex_name}')
load_image = ['-run']
if self.erase:
load_image += ['-erase']
else:
load_image += ['-noerase']
if self.verify:
load_image += ['-v']
# Load image
load_image += ['-p', '-file', hex_name]
port = ['-port', self.port]
if self.speed:
port += ['-s', self.speed]
device = ['-device', self.device]
cmd = self.rfp_cmd + port + device + load_image
self.check_call(cmd)

View file

@ -46,6 +46,7 @@ def test_runner_imports():
'qemu',
'renode',
'renode-robot',
'rfp',
'silabs_commander',
'spi_burn',
'stm32cubeprogrammer',

View file

@ -0,0 +1,256 @@
# Copyright (c) 2025 Pete Johanson
#
# SPDX-License-Identifier: Apache-2.0
import argparse
import os
from unittest.mock import call, patch
from conftest import RC_KERNEL_HEX
from runners.rfp import RfpBinaryRunner
TEST_RFP_PORT = 'test-rfp-serial'
TEST_RFP_PORT_SPEED = '115200'
TEST_RFP_DEVICE = 'RA'
TEST_RFP_USR_LOCAL_RFP_CLI = '/usr/local/bin/rfp-cli'
EXPECTED_COMMANDS = [
[
'rfp-cli',
'-port',
TEST_RFP_PORT,
'-device',
TEST_RFP_DEVICE,
'-run',
'-noerase',
'-p',
'-file',
RC_KERNEL_HEX,
],
]
EXPECTED_COMMANDS_WITH_SPEED = [
[
'rfp-cli',
'-port',
TEST_RFP_PORT,
'-s',
TEST_RFP_PORT_SPEED,
'-device',
TEST_RFP_DEVICE,
'-run',
'-noerase',
'-p',
'-file',
RC_KERNEL_HEX,
],
]
EXPECTED_COMMANDS_WITH_ERASE = [
[
'rfp-cli',
'-port',
TEST_RFP_PORT,
'-device',
TEST_RFP_DEVICE,
'-run',
'-erase',
'-p',
'-file',
RC_KERNEL_HEX,
],
]
EXPECTED_COMMANDS_WITH_VERIFY = [
[
'rfp-cli',
'-port',
TEST_RFP_PORT,
'-device',
TEST_RFP_DEVICE,
'-run',
'-noerase',
'-v',
'-p',
'-file',
RC_KERNEL_HEX,
],
]
EXPECTED_COMMANDS_WITH_RFP_CLI = [
[
TEST_RFP_USR_LOCAL_RFP_CLI,
'-port',
TEST_RFP_PORT,
'-device',
TEST_RFP_DEVICE,
'-run',
'-noerase',
'-p',
'-file',
RC_KERNEL_HEX,
],
]
def require_patch(program):
assert program in ['rfp']
os_path_isfile = os.path.isfile
def os_path_isfile_patch(filename):
if filename == RC_KERNEL_HEX:
return True
return os_path_isfile(filename)
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
@patch('runners.core.ZephyrBinaryRunner.check_call')
def test_rfp_init(cc, req, runner_config, tmpdir):
"""
Test commands using a runner created by constructor.
Input:
port=A
device=B
Output:
-port A
-device B
"""
runner = RfpBinaryRunner(runner_config, port=TEST_RFP_PORT, device=TEST_RFP_DEVICE)
with patch('os.path.isfile', side_effect=os_path_isfile_patch):
runner.run('flash')
assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS]
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
@patch('runners.core.ZephyrBinaryRunner.check_call')
def test_rfp_create(cc, req, runner_config, tmpdir):
"""
Test commands using a runner created from command line parameters.
Input:
---port A
--device B
Output:
-port A
-device B
"""
args = ['--port', str(TEST_RFP_PORT), "--device", str(TEST_RFP_DEVICE)]
parser = argparse.ArgumentParser(allow_abbrev=False)
RfpBinaryRunner.add_parser(parser)
arg_namespace = parser.parse_args(args)
runner = RfpBinaryRunner.create(runner_config, arg_namespace)
with patch('os.path.isfile', side_effect=os_path_isfile_patch):
runner.run('flash')
assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS]
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
@patch('runners.core.ZephyrBinaryRunner.check_call')
def test_rfp_create_with_speed(cc, req, runner_config, tmpdir):
"""
Test commands using a runner created from command line parameters.
Input:
--port
--speed
--speed
Output:
-s SPEED
"""
args = [
'--device',
str(TEST_RFP_DEVICE),
'--port',
str(TEST_RFP_PORT),
'--speed',
str(TEST_RFP_PORT_SPEED),
]
parser = argparse.ArgumentParser(allow_abbrev=False)
RfpBinaryRunner.add_parser(parser)
arg_namespace = parser.parse_args(args)
runner = RfpBinaryRunner.create(runner_config, arg_namespace)
with patch('os.path.isfile', side_effect=os_path_isfile_patch):
runner.run('flash')
assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS_WITH_SPEED]
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
@patch('runners.core.ZephyrBinaryRunner.check_call')
def test_rfp_create_with_erase(cc, req, runner_config, tmpdir):
"""
Test commands using a runner created from command line parameters.
Input:
--erase
Output:
-erase
"""
args = ['--device', str(TEST_RFP_DEVICE), '--port', str(TEST_RFP_PORT), '--erase']
parser = argparse.ArgumentParser(allow_abbrev=False)
RfpBinaryRunner.add_parser(parser)
arg_namespace = parser.parse_args(args)
runner = RfpBinaryRunner.create(runner_config, arg_namespace)
with patch('os.path.isfile', side_effect=os_path_isfile_patch):
runner.run('flash')
assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS_WITH_ERASE]
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
@patch('runners.core.ZephyrBinaryRunner.check_call')
def test_rfp_create_with_verify(cc, req, runner_config, tmpdir):
"""
Test commands using a runner created from command line parameters.
Input:
--verify
Output:
-v
"""
args = ['--device', str(TEST_RFP_DEVICE), '--port', str(TEST_RFP_PORT), '--verify']
parser = argparse.ArgumentParser(allow_abbrev=False)
RfpBinaryRunner.add_parser(parser)
arg_namespace = parser.parse_args(args)
runner = RfpBinaryRunner.create(runner_config, arg_namespace)
with patch('os.path.isfile', side_effect=os_path_isfile_patch):
runner.run('flash')
assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS_WITH_VERIFY]
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
@patch('runners.core.ZephyrBinaryRunner.check_call')
def test_rfp_create_with_rfp_cli(cc, req, runner_config, tmpdir):
"""
Test commands using a runner created from command line parameters.
Input:
--rfp-cli /usr/local/bin/rfp-cli
Output:
/usr/local/bin/rfp-cli
"""
args = [
'--device',
str(TEST_RFP_DEVICE),
'--port',
str(TEST_RFP_PORT),
'--rfp-cli',
str(TEST_RFP_USR_LOCAL_RFP_CLI),
]
parser = argparse.ArgumentParser(allow_abbrev=False)
RfpBinaryRunner.add_parser(parser)
arg_namespace = parser.parse_args(args)
runner = RfpBinaryRunner.create(runner_config, arg_namespace)
with patch('os.path.isfile', side_effect=os_path_isfile_patch):
runner.run('flash')
assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS_WITH_RFP_CLI]