Skip to content

Commit

Permalink
rp2040: Unify board selection cmdline args
Browse files Browse the repository at this point in the history
Fix: b/348067379
Change-Id: I0fa21d1cff72a2225286b471e1b85c3613b8b8e1
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/217716
Lint: Lint 🤖 <[email protected]>
Commit-Queue: Taylor Cramer <[email protected]>
Presubmit-Verified: CQ Bot Account <[email protected]>
Reviewed-by: Erik Gilling <[email protected]>
  • Loading branch information
cramertj authored and CQ Bot Account committed Jun 25, 2024
1 parent e6665db commit 1ca76fa
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 154 deletions.
92 changes: 56 additions & 36 deletions targets/rp2040/py/rp2040_utils/flasher.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ def _load_picotool_binary(board_info: PicoBoardInfo, binary: Path) -> bool:
_PICOTOOL_COMMAND,
'load',
'-x',
str(binary),
# We use the absolute path since `cwd` is changed below.
str(binary.absolute()),
]

# If the binary has not file extension, assume that it is ELF and
Expand Down Expand Up @@ -247,9 +248,8 @@ def _load_debugprobe_binary(
return True


def _parse_args():
"""Parses command-line arguments."""

def create_flash_parser() -> argparse.ArgumentParser:
"""Returns a parser for flashing command-line arguments."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('binary', type=Path, help='The target binary to flash')
parser.add_argument(
Expand Down Expand Up @@ -290,53 +290,73 @@ def _parse_args():
help='Output additional logs as the script runs',
)

return parser.parse_args()
return parser


def main():
"""Flash a binary."""
args = _parse_args()
log_level = logging.DEBUG if args.verbose else logging.INFO
pw_cli.log.install(level=log_level)
def device_from_args(
args: argparse.Namespace, interactive: bool
) -> PicoBoardInfo:
"""Select a PicoBoardInfo using the provided `args`.
This function will exit if no compatible board is discovered.
Args:
args: The parsed args namespace. This must be a set of arguments parsed
using `create_flash_parser`.
interactive: If true, multiple detected boards will result in a user
interaction to select which to use. If false, the first compatible
board will be used.
Returns:
Selected PicoBoardInfo.
"""
if args.pico_only and args.debug_probe_only:
_LOG.critical('Cannot specify both --pico-only and --debug-probe-only')
sys.exit(1)

# For now, require manual configurations to be fully specified.
if (args.usb_port is not None or args.usb_bus is not None) and not (
args.usb_port is not None and args.usb_bus is not None
):
if (args.usb_port is None) != (args.usb_bus is None):
_LOG.critical(
'Must specify BOTH --usb-bus and --usb-port when manually '
'specifying a device'
)
sys.exit(1)

if args.usb_bus:
board = device_detector.board_from_usb_port(args.usb_bus, args.usb_port)
else:
_LOG.debug('Attempting to automatically detect dev board')
boards = device_detector.detect_boards(
include_picos=not args.debug_probe_only,
include_debug_probes=not args.pico_only,
if args.usb_bus and args.usb_port:
return device_detector.board_from_usb_port(args.usb_bus, args.usb_port)

_LOG.debug('Attempting to automatically detect dev board')
boards = device_detector.detect_boards(
include_picos=not args.debug_probe_only,
include_debug_probes=not args.pico_only,
)
if not boards:
_LOG.error('Could not find an attached device')
sys.exit(1)
if len(boards) == 1:
_LOG.info('Only one device detected.')
return boards[0]
if not interactive:
_LOG.info(
'Interactive mode disabled. Defaulting to first discovered device.'
)
if not boards:
_LOG.error('Could not find an attached device')
sys.exit(1)
if len(boards) == 1:
_LOG.info('Only one device detected.')
board = boards[0]
else:
print('Multiple devices detected. Please select one:')
for n, board_n in enumerate(boards):
print(f' {n}: bus {board_n.bus}, port {board_n.port}')
print()
user_input = input('--> Board index (default: 0): ')
if user_input == '':
board = boards[0]
else:
board = boards[int(user_input)]
return boards[0]
print('Multiple devices detected. Please select one:')
for n, board_n in enumerate(boards):
print(f' {n}: bus {board_n.bus}, port {board_n.port}')
print()
user_input = input('--> Board index (default: 0): ')
if user_input == '':
return boards[0]
return boards[int(user_input)]


def main():
"""Flash a binary."""
args = create_flash_parser().parse_args()
log_level = logging.DEBUG if args.verbose else logging.INFO
pw_cli.log.install(level=log_level)
board = device_from_args(args, interactive=True)
_LOG.info('Flashing bus %s port %s', board.bus, board.port)
flashed = flash(board, args.baud, args.binary)
sys.exit(0 if flashed else 1)
Expand Down
133 changes: 15 additions & 118 deletions targets/rp2040/py/rp2040_utils/rpc_unit_test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,66 +29,29 @@
from pw_tokenizer import detokenize
from pw_unit_test_proto import unit_test_pb2

from rp2040_utils import device_detector
from rp2040_utils.device_detector import PicoBoardInfo
from rp2040_utils.flasher import flash, find_elf
from rp2040_utils.flasher import (
create_flash_parser,
device_from_args,
flash,
find_elf,
)

_LOG = logging.getLogger()


def parse_args():
def create_test_runner_parser() -> argparse.ArgumentParser:
"""Parses command-line arguments."""

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'binary', type=Path, help='The target test binary to run'
)
parser.add_argument(
'--usb-bus',
type=int,
help='The bus this Pi Pico is on',
)
parser.add_argument(
'--usb-port',
type=str,
help=(
'The port chain as a colon-separated list of integers of this Pi '
'Pico on the specified USB bus (e.g. 1:4:2:2)'
),
)
parser.add_argument(
'-b',
'--baud',
type=int,
default=115200,
help='Baud rate to use for serial communication with target device',
)
parser = create_flash_parser()
parser.description = __doc__
parser.add_argument(
'--test-timeout',
type=float,
default=5.0,
help='Maximum communication delay in seconds before a '
'test is considered unresponsive and aborted',
)
parser.add_argument(
'--debug-probe-only',
action='store_true',
help='Only run tests on detected Pi Pico debug probes',
)
parser.add_argument(
'--pico-only',
action='store_true',
help='Only run tests on detected Pi Pico boards',
)
parser.add_argument(
'--verbose',
'-v',
dest='verbose',
action='store_true',
help='Output additional logs as the script runs',
)

return parser.parse_args()
return parser


def run_test_on_board(
Expand Down Expand Up @@ -126,83 +89,17 @@ def run_test_on_board(
return True


def run_device_test(
binary: Path,
test_timeout_seconds: float,
baud_rate: int,
usb_bus: int,
usb_port: str,
) -> bool:
"""Flash, run, and check an on-device test binary.
Returns true on test pass.
"""
board = device_detector.board_from_usb_port(usb_bus, usb_port)
return run_test_on_board(board, baud_rate, binary, test_timeout_seconds)


def detect_and_run_test(
binary: Path,
test_timeout_seconds: float,
baud_rate: int,
include_picos: bool = True,
include_debug_probes: bool = True,
) -> bool:
"""Detect a dev board and run a test binary on it.
Returns whether or not the test completed successfully.
"""
_LOG.debug('Attempting to automatically detect dev board')
boards = device_detector.detect_boards(
include_picos=include_picos,
include_debug_probes=include_debug_probes,
)
if not boards:
_LOG.error('Could not find an attached device')
return False
return run_test_on_board(boards[0], baud_rate, binary, test_timeout_seconds)


def main():
"""Set up runner and then flash/run device test."""
args = parse_args()

args = create_test_runner_parser().parse_args()
# Log to stdout, which will be captured by the unit test server.
pw_cli.log.install(
level=logging.DEBUG if args.verbose else logging.INFO,
)

if args.pico_only and args.debug_probe_only:
_LOG.critical('Cannot specify both --pico-only and --debug-probe-only')
sys.exit(1)

# For now, require manual configurations to be fully specified.
if (args.usb_port is not None or args.usb_bus is not None) and not (
args.usb_port is not None and args.usb_bus is not None
):
_LOG.critical(
'Must specify BOTH --usb-bus and --usb-port when manually '
'specifying a device'
)
sys.exit(1)

test_passed = False
if not args.usb_bus:
test_passed = detect_and_run_test(
args.binary,
args.test_timeout,
args.baud,
not args.debug_probe_only,
not args.pico_only,
)
else:
test_passed = run_device_test(
args.binary,
args.test_timeout,
args.baud,
args.usb_bus,
args.usb_port,
)
board = device_from_args(args, interactive=False)
test_passed = run_test_on_board(
board, args.baud, args.binary, args.test_timeout
)
sys.exit(0 if test_passed else 1)


Expand Down

0 comments on commit 1ca76fa

Please sign in to comment.