From d86cc77e68eb43c97bd93a74bae69c9b97fa48e9 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 14 Jul 2026 18:55:01 +0100 Subject: [PATCH 01/15] Add picotool help topics Move device selection options into a separate picotool help device-selection command, rather than appearing in every help output Also add a family-ids topic to list available family IDs (fixes #305) --- README.md | 613 +++--------------------------------------------------- cli.h | 12 ++ main.cpp | 117 ++++++++--- 3 files changed, 127 insertions(+), 615 deletions(-) diff --git a/README.md b/README.md index b22ba878..7216fc27 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,10 @@ COMMANDS: link Link multiple binaries into one block loop. bdev Commands related to embedded block devices +TOPICS: + device-selection Options for Target Device Selection + family-ids Valid Family IDs + Use "picotool help " for more info ``` @@ -117,34 +121,7 @@ OPTIONS: TARGET SELECTION: To target one or more connected RP-series device(s) in BOOTSEL mode (the default) - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options To target a file The file name @@ -234,34 +211,7 @@ OPTIONS: TARGET SELECTION: To target one or more connected RP-series device(s) in BOOTSEL mode (the default) - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options To target a file The file name @@ -349,34 +299,7 @@ OPTIONS: Load offset (memory address; default 0x10000000) Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` e.g. @@ -427,34 +350,7 @@ OPTIONS: -t Specify file type (uf2 | elf | bin) explicitly, ignoring file extension Source device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` e.g. first looking at what is on the device... @@ -512,34 +408,7 @@ OPTIONS: Load offset (memory address; default 0x10000000) Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ## erase @@ -573,34 +442,7 @@ OPTIONS: The upper address bound in hex Source device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` e.g. first looking at what is on the device... @@ -666,34 +508,7 @@ OPTIONS: -c Select arm | riscv CPU (if possible) Selecting the device to reboot - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ## seal @@ -861,34 +676,7 @@ OPTIONS: -m family ID (will show target partition for said family) Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ```text @@ -1136,34 +924,7 @@ SYNOPSIS: OPTIONS: Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ## otp @@ -1252,34 +1013,7 @@ OPTIONS: TARGET SELECTION: Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ```text @@ -1325,34 +1059,7 @@ OPTIONS: TARGET SELECTION: Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ### load @@ -1385,34 +1092,7 @@ OPTIONS: -t Specify file type (json | bin) explicitly, ignoring file extension Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` For example, if you wish to sign a binary and then test secure boot with it, you can run the following set of commands: @@ -1440,34 +1120,7 @@ OPTIONS: JSON file with white labelling values Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ```text @@ -1537,34 +1190,7 @@ OPTIONS: Key file (.pem) Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ```text @@ -1610,34 +1236,7 @@ OPTIONS: TARGET SELECTION: To dump the contents of a target device - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options To dump the contents of an OTP JSON file The file name @@ -1826,34 +1425,7 @@ OPTIONS: --format Format the drive if necessary (may result in data loss) Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ### mkdir @@ -1897,34 +1469,7 @@ OPTIONS: --format Format the drive if necessary (may result in data loss) Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ### cp @@ -1972,34 +1517,7 @@ OPTIONS: --format Format the drive if necessary (may result in data loss) Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ### rm @@ -2043,34 +1561,7 @@ OPTIONS: --format Format the drive if necessary (may result in data loss) Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ### cat @@ -2114,34 +1605,7 @@ OPTIONS: --format Format the drive if necessary (may result in data loss) Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ### format @@ -2181,34 +1645,7 @@ OPTIONS: --force-writeable Allow writing, even if the block device is not marked as writeable Target device selection - --bus - Filter devices by USB bus number - --address - Filter devices by USB device address - --vid - Filter by vendor id - --pid - Filter by product id - --ser - Filter by serial number - --rp2040 - Assume the device is an RP2040 - this is only required when using a custom vid/pid - with an RP2040 on Windows, and is ignored on other operating systems - -f, --force - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be rebooted back to application mode - -F, --force-no-reboot - Force a device not in BOOTSEL mode but running compatible code to reset so the - command can be executed. After executing the command (unless the command itself is - a 'reboot') the device will be left connected and accessible to picotool, but - without the USB drive mounted - --bootsel-led - Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by - RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to - BOOTSEL mode - --bootsel-led-active-low - The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) + See "picotool help device-selection" for available options ``` ## Binary Information diff --git a/cli.h b/cli.h index bfaae401..34c51b8d 100644 --- a/cli.h +++ b/cli.h @@ -653,6 +653,13 @@ namespace cli { return *this; } + // Instead of expanding this group's options in place, point the reader at + // "picotool help " for the details (see the "help_topic" command dispatch). + group &help_topic(string topic) { + _help_topic = std::move(topic); + return *this; + } + static string decorate(const matchable &e, string s) { if (e.is_optional() && !e.doc_non_optional()) { return string("[") + s + "]"; @@ -772,6 +779,10 @@ namespace cli { if (!_major_group.empty()) { major_group = _major_group; } + if (!_help_topic.empty() && !this->_force_expand_help) { + options.add(major_group, minor_group, "", "See \"picotool help " + _help_topic + "\" for available options"); + return true; + } for (const auto &e : elements) { e->get_option_help(major_group, minor_group, options); } @@ -908,6 +919,7 @@ namespace cli { private: string _major_group; + string _help_topic; group_type type; vector> elements; bool _no_match_beats_error = true; diff --git a/main.cpp b/main.cpp index 38c467b3..aa2f1be2 100644 --- a/main.cpp +++ b/main.cpp @@ -723,7 +723,34 @@ auto device_selection = #endif ", ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to BOOTSEL mode" + option("--bootsel-led-active-low").set(settings.active_low) % "The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4)" - ).min(0).doc_non_optional(true).collapse_synopsys("device-selection"); + ).min(0).doc_non_optional(true).collapse_synopsys("device-selection").help_topic("device-selection"); + +struct help_topic { + string title; + string doc; + group options; // optional, leave blank for topics without options +}; + +string family_id_help_text() { + return string("A family ID identifies the target chip and/or image type of a UF2 file, and is " + "accepted by the --family option of several commands.\n\n" + "Valid family IDs are: ") + cli::join(family_names, ", ") + ".\n\n" + "A family ID may also be given directly as a 32 bit hex value, e.g. 0x12345678."; +} + +std::map help_topics { +#if HAS_LIBUSB + { "device-selection", { + "Options for Target Device Selection", + "Options for selecting or filtering the target RP-series device(s). These options are accepted by most commands which target a device.", + group(device_selection).force_expand_help(true) + } }, +#endif + { "family-ids", { + "Valid Family IDs", + family_id_help_text() + } }, +}; #define file_types_x(i)\ (option ('t', "--type") & value("type").set(settings.file_types[i]))\ @@ -1854,12 +1881,50 @@ int parse(const int argc, char **argv) { int tab = 4; bool first = true; - auto section_header=[&](const string &name) { + auto section_header=[&](const string &name, bool upper = true) { fos.first_column(0); fos.hanging_indent(0); if (!first) fos.wrap_hard(); first = false; - fos << (uppercase(name) + ":\n"); + fos << ((upper ? uppercase(name) : name) + ":\n"); + }; + auto print_options_for=[&](const group &g) { + cli::option_map options; + g.get_option_help("", "", options); + for (const auto &major : options.contents.ordered_keys()) { + section_header(major.empty() ? "OPTIONS" : major); + bool first_opt = true; + for (const auto &minor : options.contents[major].ordered_keys()) { + fos.first_column(tab); + fos.hanging_indent(tab*2); + if (!minor.empty()) { + fos << minor << "\n"; + } else if (!first_opt) { + fos << "Other\n"; + } + first_opt = false; + for (const auto &opts : options.contents[major][minor]) { + if (!opts.first.empty()) { + fos.first_column(tab*2); + fos.hanging_indent(0); + fos << opts.first << "\n"; + fos.first_column(tab*3); + } else { + fos.first_column(tab*2); + } + fos.hanging_indent(0); + fos << opts.second << "\n"; + } + } + } + }; + auto print_help_topic=[&](const help_topic &topic) { + section_header(topic.title, false); + fos.first_column(tab); + fos.hanging_indent(0); + fos << topic.doc << "\n"; + print_options_for(topic.options); + fos.flush(); }; auto usage=[&]() { if (help_mode && selected_cmd) { @@ -1931,6 +1996,12 @@ int parse(const int argc, char **argv) { s << "\n"; fos << s.str(); }; + auto write_topic = [&](size_t max, const std::string& name, const help_topic& topic) { + fos.first_column(tab); + fos << name; + fos.first_column((int) (max + tab + 3)); + fos << topic.title << "\n"; + }; if (!selected_cmd) { size_t max = 0; section_header("COMMANDS"); @@ -1940,6 +2011,16 @@ int parse(const int argc, char **argv) { for (auto &cmd: commands) { write_command(max, cmd->name(), cmd); } + if (!help_topics.empty()) { + size_t topic_max = 0; + for (auto &topic: help_topics) { + topic_max = std::max(topic.first.size(), topic_max); + } + section_header("TOPICS"); + for (auto &topic: help_topics) { + write_topic(topic_max, topic.first, topic.second); + } + } } else if (selected_cmd->is_multi()) { section_header("SUB COMMANDS"); size_t max = 0; @@ -1975,30 +2056,7 @@ int parse(const int argc, char **argv) { fos << built_without_libusb_message; #endif } else { - cli::option_map options; - selected_cmd->get_cli().get_option_help("", "", options); - for (const auto &major : options.contents.ordered_keys()) { - section_header(major.empty() ? "OPTIONS" : major); - bool first = true; - for (const auto &minor : options.contents[major].ordered_keys()) { - fos.first_column(tab); - fos.hanging_indent(tab*2); - if (!minor.empty()) { - fos << minor << "\n"; - } else if (!first) { - fos << "Other\n"; - } - first = false; - for (const auto &opts : options.contents[major][minor]) { - fos.first_column(tab*2); - fos.hanging_indent(0); - fos << opts.first << "\n"; - fos.first_column(tab*3); - fos.hanging_indent(0); - fos << opts.second << "\n"; - } - } - } + print_options_for(selected_cmd->get_cli()); } if (!selected_cmd) { fos.first_column(0); @@ -2079,6 +2137,11 @@ int parse(const int argc, char **argv) { usage(); return 0; } else { + auto topic = help_topics.find(args[0]); + if (topic != help_topics.end()) { + print_help_topic(topic->second); + return 0; + } selected_cmd = find_command(args[0]); if (selected_cmd->is_multi() && args.size() > 1) { help_mode_prefix = selected_cmd->name() + " "; From 259f32a7979ddeace00fa26cc3417d8e768bacc7 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 14 Jul 2026 19:09:20 +0100 Subject: [PATCH 02/15] Add help topics to readme --- README.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++-- gen_help_txt.sh | 50 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7216fc27..f827de6e 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,8 @@ Use "picotool help " for more info Note commands that aren't acting on files require a device in BOOTSEL mode to be connected. -## Links to documentation for `picotool` commands -[`info`](#info) [`config`](#config) [`load`](#load) [`save`](#save) [`verify`](#verify) [`erase`](#erase) [`reboot`](#reboot) [`seal`](#seal) [`encrypt`](#encrypt) [`partition`](#partition) [`uf2`](#uf2) [`otp`](#otp) [`coprodis`](#coprodis) [`link`](#link) [`bdev`](#bdev) +## Links to documentation for `picotool` commands and help topics +[`info`](#info) [`config`](#config) [`load`](#load) [`save`](#save) [`verify`](#verify) [`erase`](#erase) [`reboot`](#reboot) [`seal`](#seal) [`encrypt`](#encrypt) [`partition`](#partition) [`uf2`](#uf2) [`otp`](#otp) [`coprodis`](#coprodis) [`link`](#link) [`bdev`](#bdev) [`device-selection`](#device-selection) [`family-ids`](#family-ids) ## Building & Installing @@ -1648,6 +1648,63 @@ OPTIONS: See "picotool help device-selection" for available options ``` +## Help topics + +`picotool help ` shows additional documentation that applies across multiple commands, rather than to a single command. + +### device-selection + +```text +$ picotool help device-selection +Options for Target Device Selection: + Options for selecting or filtering the target RP-series device(s). These options are + accepted by most commands which target a device. + +OPTIONS: + --bus + Filter devices by USB bus number + --address + Filter devices by USB device address + --vid + Filter by vendor id + --pid + Filter by product id + --ser + Filter by serial number + --rp2040 + Assume the device is an RP2040 - this is only required when using a custom vid/pid + with an RP2040 on Windows, and is ignored on other operating systems + -f, --force + Force a device not in BOOTSEL mode but running compatible code to reset so the + command can be executed. After executing the command (unless the command itself is + a 'reboot') the device will be rebooted back to application mode + -F, --force-no-reboot + Force a device not in BOOTSEL mode but running compatible code to reset so the + command can be executed. After executing the command (unless the command itself is + a 'reboot') the device will be left connected and accessible to picotool, but + without the USB drive mounted + --bootsel-led + Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by + RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to + BOOTSEL mode + --bootsel-led-active-low + The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) +``` + +### family-ids + +```text +$ picotool help family-ids +Valid Family IDs: + A family ID identifies the target chip and/or image type of a UF2 file, and is accepted by + the --family option of several commands. + + Valid family IDs are: data, absolute, rp2040, rp2350-arm-s, rp2350-arm-ns, rp2350-riscv, + cyw43-firmware. + + A family ID may also be given directly as a 32 bit hex value, e.g. 0x12345678. +``` + ## Binary Information Binary information is machine locatable and generally machine consumable. I say generally because anyone can diff --git a/gen_help_txt.sh b/gen_help_txt.sh index 82915482..69b7913f 100755 --- a/gen_help_txt.sh +++ b/gen_help_txt.sh @@ -102,17 +102,57 @@ if [ ${#missing_commands[@]} -ne 0 ]; then done fi -# Regenerate the links to documentation for picotool commands section -echo "Regenerating links to documentation for picotool commands..." +# Extract help topics to check for missing help text +echo "Extracting help topics from main.cpp..." +topics=$(sed -n '/std::map help_topics {/,/^};/p' main.cpp | \ + grep -oE '"[a-zA-Z0-9_-]+"[[:space:]]*,[[:space:]]*\{' | \ + sed -E 's/^"([a-zA-Z0-9_-]+)".*/\1/') + +echo "Checking for missing help topic sections..." +missing_topics=() +while IFS= read -r topic; do + if [ ! -z "$topic" ]; then + if ! grep -q "\$ picotool help $topic" tmp/README.md; then + missing_topics+=("$topic") + echo "Missing help text section for topic: $topic" + fi + fi +done <<< "$topics" + +# Same as for missing commands above - still fails the job later, this is just to +# provide the output to copy into the right place +if [ ${#missing_topics[@]} -ne 0 ]; then + echo "Adding missing help topic sections to end of file..." + for topic in "${missing_topics[@]}"; do + echo "Running: picotool help $topic" + picotool help "$topic" > "tmp/picotool_help_${topic// /_}.txt" + + { + printf '\n```text\n$ picotool help %s\n' "$topic" + cat "tmp/picotool_help_${topic// /_}.txt" + printf '```\n' + } >> tmp/README.md + done +fi + +# Regenerate the links to documentation for picotool commands and help topics section +echo "Regenerating links to documentation for picotool commands and help topics..." links_line=$(picotool help 2>/dev/null | \ awk '/^COMMANDS:/{f=1;next} f && /^[A-Z]/{exit} f && /^ [a-z]/{print $1}' | \ grep -vE "^(help|$(echo "$ALLOWED_MISSING_COMMANDS" | tr ' ' '|'))$" | \ while IFS= read -r cmd; do printf '[`%s`](#%s) ' "$cmd" "$cmd" done | sed 's/ $//') +topic_links_line=$(echo "$topics" | while IFS= read -r topic; do + [ -z "$topic" ] && continue + printf '[`%s`](#%s) ' "$topic" "$topic" + done | sed 's/ $//') +if [ -n "$topic_links_line" ]; then + links_line="$links_line $topic_links_line" +fi export LINKS_LINE="$links_line" perl -i -0777 -pe ' - s{(## Links to documentation for `picotool` commands\n)[^\n]+}{$1$ENV{LINKS_LINE}}; + s{(## Links to documentation for `picotool` commands and help topics\n)[^\n]+}{$1$ENV{LINKS_LINE}}; ' tmp/README.md mv tmp/README.md README.md @@ -163,8 +203,8 @@ done echo "Help text sections have been updated in README.md" -# Still fail if there are missing commands -if [ ${#missing_commands[@]} -ne 0 ]; then +# Still fail if there are missing commands or topics +if [ ${#missing_commands[@]} -ne 0 ] || [ ${#missing_topics[@]} -ne 0 ]; then echo "Failing job due to missing help text sections" exit 1 fi From 0b0a8c65118657b20c66fcc9d81bdda3b39b234b Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 15 Jul 2026 11:57:05 +0100 Subject: [PATCH 03/15] Improve device selection and forced reboot docs --- README.md | 6 +++--- main.cpp | 13 ++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f827de6e..269fb64e 100644 --- a/README.md +++ b/README.md @@ -1934,9 +1934,9 @@ purposes of binaries compiled using the [pico-sdk](https://github.com/raspberryp If your code has returned then rebooting with `-f/F` will not work - instead you can set the compile definition `PICO_ENTER_USB_BOOT_ON_EXIT` to reboot and be accessible to picotool once your code has finished execution, for example with `target_compile_definitions( PRIVATE PICO_ENTER_USB_BOOT_ON_EXIT=1)` -- Uses stdio_usb - -If your binary calls `stdio_init_all()` and you have `pico_enable_stdio_usb( 1)` in your CMakeLists.txt file then you meet -this requirement (see the [hello_usb](https://github.com/raspberrypi/pico-examples/tree/master/hello_world/usb) example) +- Uses stdio_usb or the pico_usb_reset library - +If your binary calls `stdio_init_all()`, does not use TinyUSB directly (i.e. does not link `tinyusb_device`), and you have `pico_enable_stdio_usb( 1)` in your CMakeLists.txt file, then you meet +this requirement (see the [hello_usb](https://github.com/raspberrypi/pico-examples/tree/master/hello_world/usb) example). If you are using TinyUSB directly, see the [library documentation](https://www.raspberrypi.com/documentation/pico-sdk/high_level.html#group_pico_usb_reset) or the [dev_multi_cdc](https://github.com/raspberrypi/pico-examples/tree/master/usb/device/dev_multi_cdc) example for more details on how to enable the `pico_usb_reset` library. ### Issues diff --git a/main.cpp b/main.cpp index aa2f1be2..5363ce25 100644 --- a/main.cpp +++ b/main.cpp @@ -741,8 +741,11 @@ string family_id_help_text() { std::map help_topics { #if HAS_LIBUSB { "device-selection", { - "Options for Target Device Selection", - "Options for selecting or filtering the target RP-series device(s). These options are accepted by most commands which target a device.", + "Options for Target Device Selection and Rebooting", + "Options for selecting or filtering the target RP-series device(s), including rebooting devices to BOOTSEL mode first. " + "These options are accepted by most commands which target a device.\n\n" + "For the `-f/F` options, compatible code is defined as code which uses stdio_usb or the pico_usb_reset library, and which is still running. " + "See Forced Reboots in the README (https://github.com/raspberrypi/picotool#forced-reboots) for more information.", group(device_selection).force_expand_help(true) } }, #endif @@ -876,7 +879,7 @@ struct info_command : public cmd { ).min(0).doc_non_optional(true) % "Information to display" + ( #if HAS_LIBUSB - device_selection % "To target one or more connected RP-series device(s) in BOOTSEL mode (the default)" | + device_selection % "To target one or more connected RP-series device(s) (the default)" | #endif file_selection % "To target a file" ).major_group("TARGET SELECTION").min(0).doc_non_optional(true) @@ -911,7 +914,7 @@ struct config_command : public cmd { (option('g', "--group") & value("group").set(settings.config.group)) % "Filter by feature group" + ( #if HAS_LIBUSB - device_selection % "To target one or more connected RP-series device(s) in BOOTSEL mode (the default)" | + device_selection % "To target one or more connected RP-series device(s) (the default)" | #endif file_selection % "To target a file" ).major_group("TARGET SELECTION").min(0).doc_non_optional(true) @@ -1165,7 +1168,7 @@ struct erase_command : public cmd { ).min(0).doc_non_optional(true) ).min(0).doc_non_optional(true).no_match_beats_error(false) % "Selection of data to erase" + ( // note this parenthesis seems to help with error messages for say erase --foo - device_selection % "Source device selection" + device_selection % "Target device selection" ) ); } From b2a1c6fdf855defc7a4dd8b92829518c34b6a834 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 15 Jul 2026 12:23:31 +0100 Subject: [PATCH 04/15] Add "picotool help " --- README.md | 21 +++++++++++++-------- main.cpp | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 269fb64e..05f1861c 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,10 @@ COMMANDS: bdev Commands related to embedded block devices TOPICS: - device-selection Options for Target Device Selection + device-selection Options for Target Device Selection and Rebooting family-ids Valid Family IDs -Use "picotool help " for more info +Use "picotool help " or "picotool help " for more info ``` Note commands that aren't acting on files require a device in BOOTSEL mode to be connected. @@ -120,7 +120,7 @@ OPTIONS: Include all information TARGET SELECTION: - To target one or more connected RP-series device(s) in BOOTSEL mode (the default) + To target one or more connected RP-series device(s) (the default) See "picotool help device-selection" for available options To target a file @@ -210,7 +210,7 @@ OPTIONS: Filter by feature group TARGET SELECTION: - To target one or more connected RP-series device(s) in BOOTSEL mode (the default) + To target one or more connected RP-series device(s) (the default) See "picotool help device-selection" for available options To target a file @@ -441,7 +441,7 @@ OPTIONS: The lower address bound in hex The upper address bound in hex - Source device selection + Target device selection See "picotool help device-selection" for available options ``` @@ -1656,9 +1656,14 @@ OPTIONS: ```text $ picotool help device-selection -Options for Target Device Selection: - Options for selecting or filtering the target RP-series device(s). These options are - accepted by most commands which target a device. +Options for Target Device Selection and Rebooting: + Options for selecting or filtering the target RP-series device(s), including rebooting + devices to BOOTSEL mode first. These options are accepted by most commands which target a + device. + + For the `-f/F` options, compatible code is defined as code which uses stdio_usb or the + pico_usb_reset library, and which is still running. See Forced Reboots in the README + (https://github.com/raspberrypi/picotool#forced-reboots) for more information. OPTIONS: --bus diff --git a/main.cpp b/main.cpp index 5363ce25..26cb9d9c 100644 --- a/main.cpp +++ b/main.cpp @@ -2065,7 +2065,7 @@ int parse(const int argc, char **argv) { fos.first_column(0); fos.hanging_indent(0); fos.wrap_hard(); - fos << "Use \"picotool help \" for more info\n"; + fos << "Use \"picotool help \" or \"picotool help \" for more info\n"; #if !HAS_LIBUSB if (!help_mode) { fos << built_without_libusb_message; From d9fe50d253408566b457565b4336fff6997861f5 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 15 Jul 2026 12:36:34 +0100 Subject: [PATCH 05/15] Tidy up bdev docs, now bdev commands have been added --- README.md | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 05f1861c..e27eda8c 100644 --- a/README.md +++ b/README.md @@ -1897,27 +1897,13 @@ quotes, newlines etc you may have better luck defining via bi_decl in the code. ### Block devices MicroPython and CircuitPython, eventually the SDK and others may support one or more storage devices in flash. We already -have macros to define these although picotool doesn't do anything with them yet... but backup/restore/file copy and even fuse mount -in the future might be interesting. +have macros to define these, and they can be accessed with the [`bdev`](#bdev) commands. In the future backup/restore and even fuse mount might be interesting additions. -I suggest we tag these now... - -This is what I have right now off the top of my head (at the time) +These can be tagged using the `bi_block_device` macro: ```c #define bi_block_device(_tag, _name, _offset, _size, _extra, _flags) ``` -with the data going into -```c -typedef struct __packed _binary_info_block_device { - struct _binary_info_core core; - bi_ptr_of(const char) name; // optional static name (independent of what is formatted) - uint32_t offset; - uint32_t size; - bi_ptr_of(binary_info_t) extra; // additional info - uint16_t flags; -} binary_info_block_device_t; -``` -and +using the following flags ```c enum { BINARY_INFO_BLOCK_DEV_FLAG_READ = 1 << 0, // if not readable, then it is basically hidden, but tools may choose to avoid overwriting it @@ -1930,6 +1916,7 @@ enum { BINARY_INFO_BLOCK_DEV_FLAG_PT_NONE = 3 << 4, // no partition table }; ``` +For more information see the Pico SDK, where this macro and flags are defined. ### Forced Reboots From a2a9e11c025441b9abf60f920b5112ed030e5d08 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 16 Jul 2026 11:55:44 +0100 Subject: [PATCH 06/15] Review fixups Add docs for `version` and remove ALLOWED_MISSING_COMMANDS "Post load actions" -> "Load options" set/get -> get/set Fix white-label -s option not showing up Fix otp permissions docs --- README.md | 69 ++++++++++++++++++++++++++++++++++++++++++++----- gen_help_txt.sh | 10 +++---- main.cpp | 7 +++-- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index e27eda8c..f860c9a9 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Use "picotool help " or "picotool help " for more info Note commands that aren't acting on files require a device in BOOTSEL mode to be connected. ## Links to documentation for `picotool` commands and help topics -[`info`](#info) [`config`](#config) [`load`](#load) [`save`](#save) [`verify`](#verify) [`erase`](#erase) [`reboot`](#reboot) [`seal`](#seal) [`encrypt`](#encrypt) [`partition`](#partition) [`uf2`](#uf2) [`otp`](#otp) [`coprodis`](#coprodis) [`link`](#link) [`bdev`](#bdev) [`device-selection`](#device-selection) [`family-ids`](#family-ids) +[`version`](#version) [`info`](#info) [`config`](#config) [`load`](#load) [`save`](#save) [`verify`](#verify) [`erase`](#erase) [`reboot`](#reboot) [`seal`](#seal) [`encrypt`](#encrypt) [`partition`](#partition) [`uf2`](#uf2) [`otp`](#otp) [`coprodis`](#coprodis) [`link`](#link) [`bdev`](#bdev) [`device-selection`](#device-selection) [`family-ids`](#family-ids) ## Building & Installing @@ -83,6 +83,59 @@ If you don't want to build picotool yourself, you can find pre-built executables If you do wish to build picotool yourself, then see [Building](BUILDING.md#building) for build instructions. For the Pico SDK to find your picotool you will need to install it, the simplest way being to run `cmake --install .` - see [Installing](BUILDING.md#installing-so-the-pico-sdk-can-find-it) for more details and alternatives. **You cannot just copy the binary into your `PATH`, else the Pico SDK will not be able to locate it.** +## version + +Displays the picotool version, and also allows checking compatibility with an expected `picotool` version, for example to check a newer `picotool` is backwards compatible with an older version. + +```text +$ picotool help version +VERSION: + Display picotool version + +SYNOPSIS: + picotool version [-s] [] + +OPTIONS: + -s, --semantic + Output semantic version number only + + Check compatibility with version +``` + +The normal output is this format: +```text +$ picotool version +picotool v2.3.0 (Linux, GNU-12.2.0, Release) +``` + +When built without libusb you get an additional message: +```text +$ picotool version +picotool v2.3.0 (Linux, GNU-12.2.0, Release) + +This version of picotool was compiled without USB support. Some commands are not available. +``` + +The semantic output is: +```text +$ picotool version -s +2.3.0 +``` + +When checking compatibility with a compatible version (e.g. 2.2.0 with version 2.3.0): +```text +$ picotool version -s 2.2.0 +2.3.0 +``` + +Or with an incompatible version (e.g. 2.4.0 with version 2.3.0), it will exit with error code `ERROR_INCOMPATIBLE` (-3): +```text +$ picotool version -s 2.4.0 +2.3.0 +ERROR: Version 2.4.0 not compatible with this software + +``` + ## info There is _Binary Information_ support in the SDK which allows for easily storing compact information that `picotool` @@ -262,7 +315,7 @@ SYNOPSIS: [-v] [-x] [-t ] [-o ] [device-selection] OPTIONS: - Post load actions + Load options --ignore-partitions When writing flash data, ignore the partition table and write to absolute space --family @@ -948,7 +1001,7 @@ SYNOPSIS: [device-selection] picotool otp load [-r] [-e] [-s ] [-i ] [-t ] [device-selection] - picotool otp white-label -s [device-selection] + picotool otp white-label [-s ] [device-selection] picotool otp permissions [--led ] [--hash] [--sign] [] [device-selection] picotool otp dump [-r] [-e] [-p] [--output ] [device-selection] @@ -966,9 +1019,9 @@ SUB COMMANDS: list List matching known registers/fields ``` -### set/get +### get/set -These commands will set/get specific rows of OTP. By default, they will write/read all redundant rows, but this can be overridden with the `-c` argument +These commands will get/set specific rows of OTP. By default, they will read/write all redundant rows, but this can be overridden with the `-c` argument ```text $ picotool help otp get @@ -1114,9 +1167,11 @@ OTP WHITE-LABEL: Set the white labelling values in OTP SYNOPSIS: - picotool otp white-label -s [device-selection] + picotool otp white-label [-s ] [device-selection] OPTIONS: + -s + Start row for white label struct (default 0x100) (note use 0x for hex) JSON file with white labelling values Target device selection @@ -1163,7 +1218,7 @@ Device Descriptor: ### permissions -This command will run a binary on your device in order to set the OTP permissions, as these are not directly accessible from `picotool` due to errata RP2350-E15 on RP2350 A2. +This command will run a binary on your device in order to set the OTP permissions, which may not be accessible in BOOTSEL mode. For example on RP2350 A2, the permissions for pages 32-63 are not writeable from BOOTSEL mode due to errata RP2350-E15. You may also have previously configured the permissions to block writes in BOOTSEL mode using `LOCK_BL`, but now wish to lock them down further. Because it runs a binary, the binary needs to be signed if secure boot is enabled. The binary will light an LED when running, which can be configured using the `--led` argument. You can define your OTP permissions in a json file, an example of which is in [sample-permissions.json](json/sample-permissions.json). The schema for this JSON file is [here](json/schemas/permissions-schema.json) diff --git a/gen_help_txt.sh b/gen_help_txt.sh index 69b7913f..11f3a74e 100755 --- a/gen_help_txt.sh +++ b/gen_help_txt.sh @@ -1,7 +1,5 @@ #!/bin/bash -ALLOWED_MISSING_COMMANDS="version" - if ! command -v picotool; then echo "Failing job due to picotool command not being in the PATH" exit 1 @@ -78,10 +76,8 @@ missing_commands=() while IFS= read -r cmd; do if [ ! -z "$cmd" ]; then if ! grep -q "\$ picotool help $cmd" tmp/README.md; then - if [[ ! "$ALLOWED_MISSING_COMMANDS" =~ "$cmd" ]]; then - missing_commands+=("$cmd") - echo "Missing help text section for command: $cmd" - fi + missing_commands+=("$cmd") + echo "Missing help text section for command: $cmd" fi fi done <<< "$commands" @@ -139,7 +135,7 @@ fi echo "Regenerating links to documentation for picotool commands and help topics..." links_line=$(picotool help 2>/dev/null | \ awk '/^COMMANDS:/{f=1;next} f && /^[A-Z]/{exit} f && /^ [a-z]/{print $1}' | \ - grep -vE "^(help|$(echo "$ALLOWED_MISSING_COMMANDS" | tr ' ' '|'))$" | \ + grep -vE "^(help)$" | \ while IFS= read -r cmd; do printf '[`%s`](#%s) ' "$cmd" "$cmd" done | sed 's/ $//') diff --git a/main.cpp b/main.cpp index 26cb9d9c..909e9264 100644 --- a/main.cpp +++ b/main.cpp @@ -1134,7 +1134,7 @@ struct load_command : public cmd { option('u', "--update").set(settings.load.update) % "Skip writing flash sectors that already contain identical data" + option('v', "--verify").set(settings.load.verify) % "Verify the data was written correctly" + option('x', "--execute").set(settings.load.execute) % "Perform a bootrom reboot to execute the downloaded file as a program after the load - either a flash update boot for binaries in flash, or a RAM image boot for other binaries " - ).min(0).doc_non_optional(true) % "Post load actions" + + ).min(0).doc_non_optional(true) % "Load options" + file_selection % "File to load from" + ( option('o', "--offset").set(settings.offset_set) % "Specify the load address for a BIN file" & @@ -1563,9 +1563,7 @@ struct otp_white_label_command : public cmd { group get_cli() override { return ( - ( - (option('s', "--start_row") & integer("row").set(settings.otp.row)) % "Start row for white label struct (default 0x100) (note use 0x for hex)" - ).min(0).doc_non_optional(true) % "Row options" + + (option('s', "--start_row") & integer("row").set(settings.otp.row)) % "Start row for white label struct (default 0x100) (note use 0x for hex)" + named_untyped_file_selection_x("filename", 0) % "JSON file with white labelling values" + device_selection % "Target device selection" ); @@ -2142,6 +2140,7 @@ int parse(const int argc, char **argv) { } else { auto topic = help_topics.find(args[0]); if (topic != help_topics.end()) { + selected_cmd = nullptr; print_help_topic(topic->second); return 0; } From 8b836f22a408da839b1809adcb35c752f39ac5d7 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 16 Jul 2026 13:50:46 +0100 Subject: [PATCH 07/15] Review fixups gen_help_txt.sh tidyups Add topic mention to picotool help docs Missing comma in block devices docs --- README.md | 4 ++-- gen_help_txt.sh | 16 +++++++++++----- main.cpp | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f860c9a9..9a86da54 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ SYNOPSIS: picotool bdev ls|mkdir|cp|rm|cat|format COMMANDS: - help Show general help or help for a specific command + help Show general help, or help for a specific command or topic version Display picotool version info Display information from the target device(s) or file. Without any arguments, this will display basic information for all connected @@ -1952,7 +1952,7 @@ quotes, newlines etc you may have better luck defining via bi_decl in the code. ### Block devices MicroPython and CircuitPython, eventually the SDK and others may support one or more storage devices in flash. We already -have macros to define these, and they can be accessed with the [`bdev`](#bdev) commands. In the future backup/restore and even fuse mount might be interesting additions. +have macros to define these, and they can be accessed with the [`bdev`](#bdev) commands. In the future, backup/restore and even fuse mount might be interesting additions. These can be tagged using the `bi_block_device` macro: ```c diff --git a/gen_help_txt.sh b/gen_help_txt.sh index 11f3a74e..8b86554f 100755 --- a/gen_help_txt.sh +++ b/gen_help_txt.sh @@ -26,8 +26,8 @@ while IFS= read -r line; do # Replace the old section with the new one escaped_line=$(echo "$line" | sed 's/[.*+?^${}()|[]/\\&/g') - perl -i -pe ' - BEGIN { $/ = undef; $new = `cat tmp/new_section.txt`; } + perl -i -0777 -pe ' + BEGIN { $new = `cat tmp/new_section.txt`; } s/```text\n'"$escaped_line"'\n.*?\n```/$new/s; ' tmp/README.md fi @@ -159,7 +159,7 @@ echo "Checking command order..." order_mismatch=false -check_cmd_order() { +check_cmd_topic_order() { local label="$1" tool_order="$2" readme_order="$3" local fp="" fr="" while IFS= read -r cmd; do @@ -184,7 +184,13 @@ tool_top_order=$(picotool help 2>/dev/null | \ readme_top_order=$(grep -n '^\$ picotool help [a-z][a-z-]*$' README.md | \ sed 's/^\([0-9]*\):.*\$ picotool help \([a-z][a-z-]*\)$/\1 \2/' | \ sort -n | awk '{print $2}' | awk '!seen[$0]++') -check_cmd_order "top-level commands" "$tool_top_order" "$readme_top_order" +check_cmd_topic_order "top-level commands" "$tool_top_order" "$readme_top_order" + +# Check help topic order (help_topics is a std::map, so picotool prints topics +# alphabetically by key rather than in main.cpp source order) +tool_topic_order=$(picotool help 2>/dev/null | \ + awk '/^TOPICS:/{f=1;next} f && /^[A-Z]/{exit} f && /^ [a-z]/{print $1}') +check_cmd_topic_order "help topics" "$tool_topic_order" "$readme_top_order" # Check sub-command order for each parent command for array in $sub_command_arrays; do @@ -194,7 +200,7 @@ for array in $sub_command_arrays; do readme_sub_order=$(grep -n "^\$ picotool help ${prefix} [a-z][a-z-]*$" README.md | \ sed "s/^\([0-9]*\):.*\$ picotool help ${prefix} \([a-z][a-z-]*\)$/\1 \2/" | \ sort -n | awk '{print $2}' | awk '!seen[$0]++') - check_cmd_order "${prefix} sub-commands" "$tool_sub_order" "$readme_sub_order" + check_cmd_topic_order "${prefix} sub-commands" "$tool_sub_order" "$readme_sub_order" done echo "Help text sections have been updated in README.md" diff --git a/main.cpp b/main.cpp index 909e9264..aa2b89ac 100644 --- a/main.cpp +++ b/main.cpp @@ -1738,7 +1738,7 @@ struct help_command : public cmd { } string get_doc() const override { - return "Show general help or help for a specific command"; + return "Show general help, or help for a specific command or topic"; } }; From 642597a84d5f44cd0cbe9abb7df002365ea149e2 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 16 Jul 2026 15:35:34 +0100 Subject: [PATCH 08/15] Fix some options requiring separate lines Fixes things like --family appearing on separate lines Also ensures short and long options are shown in the help output --- README.md | 281 +++++++++++++++++++----------------------------------- cli.h | 27 +++++- main.cpp | 138 ++++++++++++++------------- 3 files changed, 194 insertions(+), 252 deletions(-) diff --git a/README.md b/README.md index 9a86da54..cc72052a 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ TARGET SELECTION: To target a file The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension ``` @@ -255,11 +255,9 @@ SYNOPSIS: picotool config [-s ] [-g ] [-t ] OPTIONS: - - Variable name - - New value - -g + -s, --set + Set config variable name to new value + -g, --group Filter by feature group TARGET SELECTION: @@ -268,7 +266,7 @@ TARGET SELECTION: To target a file The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension ``` @@ -318,14 +316,10 @@ OPTIONS: Load options --ignore-partitions When writing flash data, ignore the partition table and write to absolute space - --family + --family Specify the family ID of the file to load - - family ID to use for load - -p, --partition + -p, --partition Specify the partition to load into - - partition to load into -n, --no-overwrite When writing flash data, do not overwrite an existing program in flash. If picotool cannot determine the size/presence of the program in flash, the command fails @@ -344,13 +338,11 @@ OPTIONS: File to load from The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension BIN file options - -o, --offset - Specify the load address for a BIN file - - Load offset (memory address; default 0x10000000) + -o, --offset + Specify the load address for a BIN file (memory address; default 0x10000000) Target device selection See "picotool help device-selection" for available options ``` @@ -393,14 +385,12 @@ OPTIONS: Other -v, --verify Verify the data was saved correctly - --family + --family Specify the family ID to save the file as - - family ID to save file as File to save to The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension Source device selection See "picotool help device-selection" for available options @@ -447,19 +437,14 @@ OPTIONS: The file to compare against The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension Address options - -r, --range - Compare a sub range of memory only - - The lower address bound in hex - - The upper address bound in hex - -o, --offset - Specify the load address when comparing with a BIN file - - Load offset (memory address; default 0x10000000) + -r, --range + Compare a sub range of memory only (address bounds in hex) + -o, --offset + Specify the load address (memory address; default 0x10000000) when comparing with a + BIN file Target device selection See "picotool help device-selection" for available options ``` @@ -556,9 +541,9 @@ OPTIONS: Reboot back into the application (this is the default) -u, --usb Reboot back into BOOTSEL mode - -g + -g, --diagnostic Select diagnostic partition - -c + -c, --cpu Select arm | riscv CPU (if possible) Selecting the device to reboot See "picotool help device-selection" for available options @@ -615,17 +600,15 @@ OPTIONS: File to load from The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension BIN file options - -o, --offset - Specify the load address for a BIN file - - Load offset (memory address; default 0x10000000) + -o, --offset + Specify the load address for a BIN file (memory address; default 0x10000000) File to save to The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension ``` @@ -673,10 +656,9 @@ OPTIONS: Use ~180MHz ROSC configuration for embedded bootloader --use-mbedtls Use MbedTLS implementation of embedded bootloader (faster but less secure) - --otp-key-page - Specify the OTP page storing the AES key (IV salt is stored on the next page) - - OTP page (default 29) + --otp-key-page + Specify the OTP page storing the AES key (default 29; IV salt is stored on the next + page) AES Key Share or AES Key @@ -697,17 +679,15 @@ OPTIONS: File to load from The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension BIN file options - -o, --offset - Specify the load address for a BIN file - - Load offset (memory address; default 0x10000000) + -o, --offset + Specify the load address for a BIN file (memory address; default 0x10000000) File to save to The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension ``` @@ -726,7 +706,7 @@ SYNOPSIS: picotool partition info [-m ] [device-selection] OPTIONS: - -m + -m, --family family ID (will show target partition for said family) Target device selection See "picotool help device-selection" for available options @@ -776,26 +756,22 @@ OPTIONS: output file The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension UF2 output options - -o, --offset - Specify the load address for UF2 file output - - Load offset (memory address; default 0x10000000) - --family - Specify the family if for UF2 file output - - family ID for UF2 (default absolute) + -o, --offset + Specify the load address for UF2 file output (memory address; default 0x10000000) + --family + Specify the family id for UF2 file output (default absolute) embed partition table into bootloader ELF The file name - -t + -t, --type Specify file type (elf) explicitly, ignoring file extension Partition Table Options --sign The file name - -t + -t, --type Specify file type (pem) explicitly, ignoring file extension --no-hash Don't hash the partition table @@ -836,28 +812,22 @@ OPTIONS: File to load from The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension File to save UF2 to The file name - -t + -t, --type Specify file type (uf2) explicitly, ignoring file extension Packaging Options - -o, --offset - Specify the load address - - Load offset (memory address; default 0x10000000 for BIN file) + -o, --offset + Specify the load address (memory address; default 0x10000000 for BIN file) UF2 Family options - --family - Specify the family ID - - family ID for UF2 + --family + Specify the family ID for UF2 Platform options - --platform - Optional platform for memory-address validation - - platform to use (eg rp2040, rp2350) + --platform + Optional platform for memory-address validation (eg rp2040, rp2350) Errata RP2350-E10 Fix --abs-block Add an absolute block @@ -887,33 +857,27 @@ OPTIONS: First file to combine The file name - -t + -t, --type Specify file type (uf2) explicitly, ignoring file extension Second file to combine The file name - -t + -t, --type Specify file type (uf2) explicitly, ignoring file extension File to save output to The file name - -t + -t, --type Specify file type (uf2) explicitly, ignoring file extension UF2 Family options - --family - Specify the family ID - - family ID for combined UF2 (defaults to first one) + --family + Specify the family ID for combined UF2 (defaults to first one) Offset options - --offset - Offset second UF2 by amount - - offset amount (hexadecimal; default 0x0) + --offset + Offset second UF2 by amount (hexadecimal; default 0x0) Partition options - --partition + --partition Place second UF2 in partition (first UF2 must contain a partition table) - - partition number (default to 0) Errata RP2350-E10 Fix --abs-block Add an absolute block @@ -1034,7 +998,7 @@ SYNOPSIS: OPTIONS: Row/field options - -c + -c, --copies Read multiple redundant values -r, --raw Get raw 24-bit values @@ -1042,7 +1006,7 @@ OPTIONS: Use error correction -n, --no-descriptions Don't show descriptions - -i + -i, --include Include extra otp definition Row/Field Selection -z, --fuzzy @@ -1080,7 +1044,7 @@ SYNOPSIS: OPTIONS: Redundancy/Error Correction Overrides - -c + -c, --copies Write multiple redundant values -r, --raw Set raw 24-bit values @@ -1088,7 +1052,7 @@ OPTIONS: Use error correction -s, --set-bits Set bits only - -i + -i, --include Include extra otp definition The value to set @@ -1135,14 +1099,14 @@ OPTIONS: Set raw 24-bit values. This is the default for BIN files -e, --ecc Use error correction - -s + -s, --start_row Start row to load at (note use 0x for hex) - -i + -i, --include Include extra otp definition File to load row(s) from The file name - -t + -t, --type Specify file type (json | bin) explicitly, ignoring file extension Target device selection See "picotool help device-selection" for available options @@ -1170,7 +1134,8 @@ SYNOPSIS: picotool otp white-label [-s ] [device-selection] OPTIONS: - -s + Row options + -s, --start_row Start row for white label struct (default 0x100) (note use 0x for hex) JSON file with white labelling values @@ -1295,7 +1260,7 @@ TARGET SELECTION: To dump the contents of an OTP JSON file The file name - -t + -t, --type Specify file type (json) explicitly, ignoring file extension ``` @@ -1319,7 +1284,7 @@ OPTIONS: Don't show descriptions -f, --field-descriptions Show all field descriptions - -i + -i, --include Include extra otp definition The row/field selector, each of which can select a whole row: @@ -1380,17 +1345,17 @@ OPTIONS: Don't print any output --verbose Print verbose output - -p + -p, --pad Specify alignment to pad to (hexadecimal; default 0x1000) File to write to The file name - -t + -t, --type Specify file type (uf2 | bin) explicitly, ignoring file extension Files to link The file name - -t + -t, --type Specify file type (uf2 | elf | bin) explicitly, ignoring file extension The file name @@ -1457,22 +1422,14 @@ OPTIONS: -r, --recursive List files in directories recursively Block device options - -p, --partition-number + -p, --partition-number Partition number to use as block device - - partition number - --partition-name + --partition-name Partition name to use as block device - - partition name - --partition-id + --partition-id Partition ID to use as block device - - partition id - --filesystem - Specify filesystem to use - - littlefs|fatfs + --filesystem + Specify filesystem to use (littlefs|fatfs) --force-formattable Allow formatting, even if the block device is not marked as fomattable --force-writeable @@ -1501,22 +1458,14 @@ OPTIONS: The directory name Block device options - -p, --partition-number + -p, --partition-number Partition number to use as block device - - partition number - --partition-name + --partition-name Partition name to use as block device - - partition name - --partition-id + --partition-id Partition ID to use as block device - - partition id - --filesystem - Specify filesystem to use - - littlefs|fatfs + --filesystem + Specify filesystem to use (littlefs|fatfs) --force-formattable Allow formatting, even if the block device is not marked as fomattable --force-writeable @@ -1549,22 +1498,14 @@ OPTIONS: The file name Block device options - -p, --partition-number + -p, --partition-number Partition number to use as block device - - partition number - --partition-name + --partition-name Partition name to use as block device - - partition name - --partition-id + --partition-id Partition ID to use as block device - - partition id - --filesystem - Specify filesystem to use - - littlefs|fatfs + --filesystem + Specify filesystem to use (littlefs|fatfs) --force-formattable Allow formatting, even if the block device is not marked as fomattable --force-writeable @@ -1593,22 +1534,14 @@ OPTIONS: The file name Block device options - -p, --partition-number + -p, --partition-number Partition number to use as block device - - partition number - --partition-name + --partition-name Partition name to use as block device - - partition name - --partition-id + --partition-id Partition ID to use as block device - - partition id - --filesystem - Specify filesystem to use - - littlefs|fatfs + --filesystem + Specify filesystem to use (littlefs|fatfs) --force-formattable Allow formatting, even if the block device is not marked as fomattable --force-writeable @@ -1637,22 +1570,14 @@ OPTIONS: The file name Block device options - -p, --partition-number + -p, --partition-number Partition number to use as block device - - partition number - --partition-name + --partition-name Partition name to use as block device - - partition name - --partition-id + --partition-id Partition ID to use as block device - - partition id - --filesystem - Specify filesystem to use - - littlefs|fatfs + --filesystem + Specify filesystem to use (littlefs|fatfs) --force-formattable Allow formatting, even if the block device is not marked as fomattable --force-writeable @@ -1679,22 +1604,14 @@ SYNOPSIS: OPTIONS: Block device options - -p, --partition-number + -p, --partition-number Partition number to use as block device - - partition number - --partition-name + --partition-name Partition name to use as block device - - partition name - --partition-id + --partition-id Partition ID to use as block device - - partition id - --filesystem - Specify filesystem to use - - littlefs|fatfs + --filesystem + Specify filesystem to use (littlefs|fatfs) --force-formattable Allow formatting, even if the block device is not marked as fomattable --force-writeable diff --git a/cli.h b/cli.h index 34c51b8d..259be689 100644 --- a/cli.h +++ b/cli.h @@ -195,6 +195,10 @@ namespace cli { return {_name}; } + virtual string option_label() const { + return synopsys()[0]; + } + virtual bool is_optional() const { return !_min; } @@ -353,14 +357,18 @@ namespace cli { _name = short_opt.empty() ? long_opt : short_opt; } - bool get_option_help(string major_group, string minor_group, option_map &options) const override { - if (doc().empty()) return false; + string option_label() const override { string label = short_opt.empty() ? "" : _name; if (!long_opt.empty()) { if (!label.empty()) label += ", "; label += long_opt; } - options.add(major_group, minor_group, label, doc()); + return label; + } + + bool get_option_help(string major_group, string minor_group, option_map &options) const override { + if (doc().empty()) return false; + options.add(major_group, minor_group, option_label(), doc()); return true; } @@ -668,6 +676,17 @@ namespace cli { } } + string option_label() const override { + if (type != set && type != sequence) { + return matchable::option_label(); + } + vector parts; + for (auto &x : elements) { + parts.push_back(decorate(*x, x->option_label())); + } + return join(parts, " "); + } + vector synopsys() const override { vector rc; switch (type) { @@ -770,7 +789,7 @@ namespace cli { bool get_option_help(string major_group, string minor_group, option_map &options) const override { // todo beware.. this check is necessary as is, but I'm not sure what removing it breaks in terms of formatting :-( if (is_optional() && !this->_doc_non_optional && !this->_force_expand_help) { - options.add(major_group, minor_group, synopsys()[0], doc()); + options.add(major_group, minor_group, option_label(), doc()); return true; } if (!doc().empty()) { diff --git a/main.cpp b/main.cpp index aa2b89ac..d221fa35 100644 --- a/main.cpp +++ b/main.cpp @@ -848,6 +848,10 @@ std::map help_topics { auto file_types = (option ('t', "--type") & value("type").set(settings.file_types[0])) % "Specify file type (uf2 | elf | bin) explicitly, ignoring file extension"; +group as_section(const group &g) { + return group() + g; +} + auto file_selection = ( value("filename").with_exclusion_filter([](const string &value) { @@ -908,9 +912,9 @@ struct config_command : public cmd { group get_cli() override { return ( (option('s', "--set") & ( - value("key").set(settings.config.key) % "Variable name" + - value("value").set(settings.config.value) % "New value") - ).force_expand_help(true) + + value("key").set(settings.config.key) + + value("value").set(settings.config.value)) + ) % "Set config variable name to new value" + (option('g', "--group") & value("group").set(settings.config.group)) % "Filter by feature group" + ( #if HAS_LIBUSB @@ -932,14 +936,14 @@ struct config_command : public cmd { #if HAS_LIBUSB auto bdev_base_options = ( - (option('p', "--partition-number") % "Partition number to use as block device" & - integer("partition number").set(settings.bdev.partition_number) % "partition number").force_expand_help(true) + - (option("--partition-name") % "Partition name to use as block device" & - value("partition name").set(settings.bdev.partition_name) % "partition name").force_expand_help(true) + - (option("--partition-id") % "Partition ID to use as block device" & - integer("partition id").set(settings.bdev.partition_id) % "partition id").force_expand_help(true) + - (option("--filesystem") % "Specify filesystem to use" & - bdev_fs("fs").set(settings.bdev.fs) % "littlefs|fatfs").force_expand_help(true) + + (option('p', "--partition-number") & + integer("partition number").set(settings.bdev.partition_number)) % "Partition number to use as block device" + + (option("--partition-name") & + value("partition name").set(settings.bdev.partition_name)) % "Partition name to use as block device" + + (option("--partition-id") & + integer("partition id").set(settings.bdev.partition_id)) % "Partition ID to use as block device" + + (option("--filesystem") & + bdev_fs("fs").set(settings.bdev.fs)) % "Specify filesystem to use (littlefs|fatfs)" + (option("--force-formattable").set(settings.bdev.force_formattable) % "Allow formatting, even if the block device is not marked as fomattable") + (option("--force-writeable").set(settings.bdev.force_writeable) % "Allow writing, even if the block device is not marked as writeable") ).min(0).doc_non_optional(true) % "Block device options"; @@ -1073,11 +1077,11 @@ struct verify_command : public cmd { return ( file_selection % "The file to compare against" + ( - (option('r', "--range").set(settings.range_set) % "Compare a sub range of memory only" & - hex("from").set(settings.from) % "The lower address bound in hex" & - hex("to").set(settings.to) % "The upper address bound in hex").force_expand_help(true) + - (option('o', "--offset").set(settings.offset_set) % "Specify the load address when comparing with a BIN file" & - hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)").force_expand_help(true) + (option('r', "--range").set(settings.range_set) & + hex("from").set(settings.from) & + hex("to").set(settings.to)) % "Compare a sub range of memory only (address bounds in hex)" + + (option('o', "--offset").set(settings.offset_set) & + hex("offset").set(settings.offset)) % "Specify the load address (memory address; default 0x10000000) when comparing with a BIN file" ).min(0).doc_non_optional(true) % "Address options" + device_selection % "Target device selection" ); @@ -1104,8 +1108,8 @@ struct save_command : public cmd { ).min(0).doc_non_optional(true) ).min(0).doc_non_optional(true).no_match_beats_error(false) % "Selection of data to save" + option('v', "--verify").set(settings.save.verify) % "Verify the data was saved correctly" + - (option("--family") % "Specify the family ID to save the file as" & - family_id("family_id").set(settings.family_id) % "family ID to save file as").force_expand_help(true) + + (option("--family") & + family_id("family_id").set(settings.family_id)) % "Specify the family ID to save the file as" + ( // note this parenthesis seems to help with error messages for say save --foo file_selection % "File to save to" + device_selection % "Source device selection" @@ -1125,10 +1129,10 @@ struct load_command : public cmd { return ( ( option("--ignore-partitions").set(settings.load.ignore_pt) % "When writing flash data, ignore the partition table and write to absolute space" + - (option("--family") % "Specify the family ID of the file to load" & - family_id("family_id").set(settings.family_id) % "family ID to use for load").force_expand_help(true) + - (option('p', "--partition") % "Specify the partition to load into" & - integer("partition").set(settings.load.partition) % "partition to load into").force_expand_help(true) + + (option("--family") & + family_id("family_id").set(settings.family_id)) % "Specify the family ID of the file to load" + + (option('p', "--partition") & + integer("partition").set(settings.load.partition)) % "Specify the partition to load into" + option('n', "--no-overwrite").set(settings.load.no_overwrite) % "When writing flash data, do not overwrite an existing program in flash. If picotool cannot determine the size/presence of the program in flash, the command fails" + option('N', "--no-overwrite-unsafe").set(settings.load.no_overwrite_force) % "When writing flash data, do not overwrite an existing program in flash. If picotool cannot determine the size/presence of the program in flash, the load continues anyway" + option('u', "--update").set(settings.load.update) % "Skip writing flash sectors that already contain identical data" + @@ -1136,10 +1140,10 @@ struct load_command : public cmd { option('x', "--execute").set(settings.load.execute) % "Perform a bootrom reboot to execute the downloaded file as a program after the load - either a flash update boot for binaries in flash, or a RAM image boot for other binaries " ).min(0).doc_non_optional(true) % "Load options" + file_selection % "File to load from" + - ( - option('o', "--offset").set(settings.offset_set) % "Specify the load address for a BIN file" & - hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)" - ).force_expand_help(true) % "BIN file options" + + as_section( + (option('o', "--offset").set(settings.offset_set) & + hex("offset").set(settings.offset)) % "Specify the load address for a BIN file (memory address; default 0x10000000)" + ).min(0).doc_non_optional(true) % "BIN file options" + device_selection % "Target device selection" ); } @@ -1192,9 +1196,9 @@ struct encrypt_command : public cmd { option("--fast-rosc").set(settings.encrypt.fast_rosc) % "Use ~180MHz ROSC configuration for embedded bootloader" + option("--use-mbedtls").set(settings.encrypt.use_mbedtls) % "Use MbedTLS implementation of embedded bootloader (faster but less secure)" + ( - option("--otp-key-page").set(settings.encrypt.otp_key_page_set) % "Specify the OTP page storing the AES key (IV salt is stored on the next page)" & - integer("page").set(settings.encrypt.otp_key_page) % "OTP page (default 29)" - ).force_expand_help(true) + + option("--otp-key-page").set(settings.encrypt.otp_key_page_set) & + integer("page").set(settings.encrypt.otp_key_page) + ) % "Specify the OTP page storing the AES key (default 29; IV salt is stored on the next page)" + ( option("--hash").set(settings.seal.hash) % "Hash the encrypted file" + option("--sign").set(settings.seal.sign) % "Sign the encrypted file" + @@ -1202,10 +1206,10 @@ struct encrypt_command : public cmd { option("--pin-xip-sram").set(settings.seal.pin_xip_sram) % "Pin XIP SRAM on load" ).min(0).doc_non_optional(true) % "Signing Configuration" + named_file_selection_x("infile", 0) % "File to load from" + - ( - option('o', "--offset").set(settings.offset_set) % "Specify the load address for a BIN file" & - hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)" - ).force_expand_help(true) % "BIN file options" + + as_section( + (option('o', "--offset").set(settings.offset_set) & + hex("offset").set(settings.offset)) % "Specify the load address for a BIN file (memory address; default 0x10000000)" + ).min(0).doc_non_optional(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + named_untyped_file_selection_x("aes_key", 2) % "AES Key Share or AES Key" + named_untyped_file_selection_x("iv_salt", 3) % "IV Salt" + @@ -1236,10 +1240,10 @@ struct seal_command : public cmd { option("--no-squash").set(settings.seal.no_squash) % "Don't squash segments in the ELF file" ).min(0).doc_non_optional(true) % "Configuration" + named_file_selection_x("infile", 0) % "File to load from" + - ( - option('o', "--offset").set(settings.offset_set) % "Specify the load address for a BIN file" & - hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)" - ).force_expand_help(true) % "BIN file options" + + as_section( + (option('o', "--offset").set(settings.offset_set) & + hex("offset").set(settings.offset)) % "Specify the load address for a BIN file (memory address; default 0x10000000)" + ).min(0).doc_non_optional(true) % "BIN file options" + named_file_selection_x("outfile", 1) % "File to save to" + optional_untyped_file_selection_x("key", 2) % "Key file (.pem)" + optional_untyped_file_selection_x("otp", 3) % "JSON file to save OTP to (will edit existing file if it exists)" + @@ -1317,10 +1321,10 @@ struct partition_create_command : public cmd { named_untyped_file_selection_x("infile", 0) % "partition table JSON" + (named_file_selection_x("outfile", 1) % "output file" + ( - (option('o', "--offset").set(settings.offset_set) % "Specify the load address for UF2 file output" & - hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000)").force_expand_help(true) + - (option("--family") % "Specify the family if for UF2 file output" & - family_id("family_id").set(settings.family_id) % "family ID for UF2 (default absolute)").force_expand_help(true) + (option('o', "--offset").set(settings.offset_set) & + hex("offset").set(settings.offset)) % "Specify the load address for UF2 file output (memory address; default 0x10000000)" + + (option("--family") & + family_id("family_id").set(settings.family_id)) % "Specify the family id for UF2 file output (default absolute)" ).min(0).force_expand_help(true) % "UF2 output options") + optional_typed_file_selection_x("bootloader", 2, "elf") % "embed partition table into bootloader ELF" + ( @@ -1563,7 +1567,9 @@ struct otp_white_label_command : public cmd { group get_cli() override { return ( - (option('s', "--start_row") & integer("row").set(settings.otp.row)) % "Start row for white label struct (default 0x100) (note use 0x for hex)" + + as_section( + (option('s', "--start_row") & integer("row").set(settings.otp.row)) % "Start row for white label struct (default 0x100) (note use 0x for hex)" + ).min(0).doc_non_optional(true) % "Row options" + named_untyped_file_selection_x("filename", 0) % "JSON file with white labelling values" + device_selection % "Target device selection" ); @@ -1623,18 +1629,18 @@ struct uf2_convert_command : public cmd { option("--verbose").set(settings.verbose) % "Print verbose output" + named_file_selection_x("infile", 0) % "File to load from" + named_typed_file_selection_x("outfile", 1, "uf2") % "File to save UF2 to" + - ( - option('o', "--offset").set(settings.offset_set) % "Specify the load address" & - hex("offset").set(settings.offset) % "Load offset (memory address; default 0x10000000 for BIN file)" - ).force_expand_help(true) % "Packaging Options" + - ( - option("--family") % "Specify the family ID" & - family_id("family_id").set(settings.family_id) % "family ID for UF2" - ).force_expand_help(true) % "UF2 Family options" + - ( - option("--platform") % "Optional platform for memory-address validation" & - platform_model("platform").set(settings.model) % "platform to use (eg rp2040, rp2350)" - ).force_expand_help(true) % "Platform options" + as_section( + (option('o', "--offset").set(settings.offset_set) & + hex("offset").set(settings.offset)) % "Specify the load address (memory address; default 0x10000000 for BIN file)" + ).min(0).doc_non_optional(true) % "Packaging Options" + + as_section( + (option("--family") & + family_id("family_id").set(settings.family_id)) % "Specify the family ID for UF2" + ).min(0).doc_non_optional(true) % "UF2 Family options" + + as_section( + (option("--platform") & + platform_model("platform").set(settings.model)) % "Optional platform for memory-address validation (eg rp2040, rp2350)" + ).min(0).doc_non_optional(true) % "Platform options" #if SUPPORT_RP2350_A2 + ( option("--abs-block").set(settings.uf2.abs_block) % "Add an absolute block" + @@ -1661,18 +1667,18 @@ struct uf2_combine_command : public cmd { named_typed_file_selection_x("infile1", 1, "uf2") % "First file to combine" + named_typed_file_selection_x("infile2", 2, "uf2") % "Second file to combine" + named_typed_file_selection_x("outfile", 0, "uf2") % "File to save output to" + - ( - option("--family") % "Specify the family ID" & - family_id("family_id").set(settings.family_id) % "family ID for combined UF2 (defaults to first one)" - ).force_expand_help(true) % "UF2 Family options" + - ( - option("--offset").set(settings.uf2.offset_set) % "Offset second UF2 by amount" & - hex("offset").set(settings.uf2.offset) % "offset amount (hexadecimal; default 0x0)" - ).force_expand_help(true) % "Offset options" + - ( - option("--partition").set(settings.uf2.partition_set) % "Place second UF2 in partition (first UF2 must contain a partition table)" & - integer("partition").min_value(0).max_value(PARTITION_TABLE_MAX_PARTITIONS-1).set(settings.uf2.partition) % "partition number (default to 0)" - ).force_expand_help(true) % "Partition options" + as_section( + (option("--family") & + family_id("family_id").set(settings.family_id)) % "Specify the family ID for combined UF2 (defaults to first one)" + ).min(0).doc_non_optional(true) % "UF2 Family options" + + as_section( + (option("--offset").set(settings.uf2.offset_set) & + hex("offset").set(settings.uf2.offset)) % "Offset second UF2 by amount (hexadecimal; default 0x0)" + ).min(0).doc_non_optional(true) % "Offset options" + + as_section( + (option("--partition").set(settings.uf2.partition_set) & + integer("partition").min_value(0).max_value(PARTITION_TABLE_MAX_PARTITIONS-1).set(settings.uf2.partition)) % "Place second UF2 in partition (first UF2 must contain a partition table)" + ).min(0).doc_non_optional(true) % "Partition options" #if SUPPORT_RP2350_A2 + ( option("--abs-block").set(settings.uf2.abs_block) % "Add an absolute block" + From cd16e24f646e648d9e5b2a320b9ec0f0f937b90e Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 16 Jul 2026 18:29:11 +0100 Subject: [PATCH 09/15] Clarify that uf2 info is RP2350-only, and add more (RP2350 only) printout Also ensure all lines end in full-stops --- README.md | 82 +++++++++++++++++++++++++++---------------------------- main.cpp | 49 ++++++++++++++++++--------------- 2 files changed, 68 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index cc72052a..0aacba59 100644 --- a/README.md +++ b/README.md @@ -44,26 +44,26 @@ SYNOPSIS: picotool bdev ls|mkdir|cp|rm|cat|format COMMANDS: - help Show general help, or help for a specific command or topic - version Display picotool version + help Show general help, or help for a specific command or topic. + version Display picotool version. info Display information from the target device(s) or file. Without any arguments, this will display basic information for all connected - RP-series devices in BOOTSEL mode + RP-series devices in BOOTSEL mode. config Display or change program configuration settings from the target device(s) or file. load Load the program / memory range stored in a file onto the device. save Save the program / memory stored in flash on the device to a file. verify Check that the device contents match those in the file. erase Erase the program / memory stored in flash on the device. - reboot Reboot the device + reboot Reboot the device. seal Add final metadata to a binary, optionally including a hash and/or signature. encrypt Encrypt the program. - partition Commands related to RP2350 Partition Tables - uf2 Commands related to UF2 creation and status - otp Commands related to the RP2350 OTP (One-Time-Programmable) Memory + partition Commands related to RP2350 Partition Tables. + uf2 Commands related to UF2 creation and status. + otp Commands related to the RP2350 OTP (One-Time-Programmable) Memory. coprodis Post-process coprocessor instructions in disassembly files. link Link multiple binaries into one block loop. - bdev Commands related to embedded block devices + bdev Commands related to embedded block devices. TOPICS: device-selection Options for Target Device Selection and Rebooting @@ -90,7 +90,7 @@ Displays the picotool version, and also allows checking compatibility with an ex ```text $ picotool help version VERSION: - Display picotool version + Display picotool version. SYNOPSIS: picotool version [-s] [] @@ -149,7 +149,7 @@ $ picotool help info INFO: Display information from the target device(s) or file. Without any arguments, this will display basic information for all connected RP-series - devices in BOOTSEL mode + devices in BOOTSEL mode. SYNOPSIS: picotool info [-b] [-m] [-p] [-d] [--debug] [-l] [-a] [device-selection] @@ -530,7 +530,7 @@ Partition 1 ```text $ picotool help reboot REBOOT: - Reboot the device + Reboot the device. SYNOPSIS: picotool reboot [-a] [-u] [-g ] [-c ] [device-selection] @@ -738,7 +738,7 @@ By default, all partition tables are hashed, and you can also sign them. The sch ```text $ picotool help partition create PARTITION CREATE: - Create a partition table from json + Create a partition table from json. SYNOPSIS: picotool partition create [--quiet] [--verbose] [-t ] [[-o @@ -929,12 +929,12 @@ $ picotool load -x combined.uf2 ### info -This command reads the information on a device about why a UF2 download has failed. It will only give information if the most recent download has failed. +This command reads the information on an RP2350 device about why a UF2 download has failed. It will only give information if the most recent download has failed. ```text $ picotool help uf2 info UF2 INFO: - Print info about UF2 download. + Print info about UF2 download. (RP2350 only) SYNOPSIS: picotool uf2 info [device-selection] @@ -956,7 +956,7 @@ For the `list`, `set`, `get` and `load` commands, you can define your own OTP la ```text $ picotool help otp OTP: - Commands related to the RP2350 OTP (One-Time-Programmable) Memory + Commands related to the RP2350 OTP (One-Time-Programmable) Memory. SYNOPSIS: picotool otp get [-c ] [-r] [-e] [-n] [-i ] [device-selection] [-z] @@ -973,14 +973,14 @@ SYNOPSIS: picotool otp list [-p] [-n] [-f] [-i ] [..] SUB COMMANDS: - get Get the value of one or more OTP registers/fields (RP2350 only) - set Set the value of an OTP row/field (RP2350 only) + get Get the value of one or more OTP registers/fields. (RP2350 only) + set Set the value of an OTP row/field. (RP2350 only) load Load the row range stored in a file into OTP and verify. Data is 2 bytes/row for ECC, 4 bytes/row for raw (MSB is ignored). (RP2350 only) - white-label Set the white labelling values in OTP (RP2350 only) - permissions Set the OTP access permissions (RP2350 only) - dump Dump entire OTP (RP2350 only) - list List matching known registers/fields + white-label Set the white labelling values in OTP. (RP2350 only) + permissions Set the OTP access permissions. (RP2350 only) + dump Dump entire OTP. (RP2350 only) + list List matching known registers/fields. ``` ### get/set @@ -990,7 +990,7 @@ These commands will get/set specific rows of OTP. By default, they will read/wri ```text $ picotool help otp get OTP GET: - Get the value of one or more OTP registers/fields + Get the value of one or more OTP registers/fields. (RP2350 only) SYNOPSIS: picotool otp get [-c ] [-r] [-e] [-n] [-i ] [device-selection] [-z] @@ -1036,7 +1036,7 @@ TARGET SELECTION: ```text $ picotool help otp set OTP SET: - Set the value of an OTP row/field + Set the value of an OTP row/field. (RP2350 only) SYNOPSIS: picotool otp set [-c ] [-r] [-e] [-s] [-i ] [-z] @@ -1087,7 +1087,7 @@ This command allows loading of a range of OTP rows onto the device. The source c $ picotool help otp load OTP LOAD: Load the row range stored in a file into OTP and verify. Data is 2 bytes/row for ECC, 4 - bytes/row for raw (MSB is ignored). + bytes/row for raw (MSB is ignored). (RP2350 only) SYNOPSIS: picotool otp load [-r] [-e] [-s ] [-i ] [-t ] @@ -1128,7 +1128,7 @@ This can be configured from a JSON file, an example of which is in [sample-wl.js ```text $ picotool help otp white-label OTP WHITE-LABEL: - Set the white labelling values in OTP + Set the white labelling values in OTP. (RP2350 only) SYNOPSIS: picotool otp white-label [-s ] [device-selection] @@ -1191,7 +1191,7 @@ is in [sample-permissions.json](json/sample-permissions.json). The schema for th ```text $ picotool help otp permissions OTP PERMISSIONS: - Set the OTP access permissions + Set the OTP access permissions. (RP2350 only) SYNOPSIS: picotool otp permissions [--led ] [--hash] [--sign] [] @@ -1237,7 +1237,7 @@ This command will read the entire OTP contents and print it out as hex. ```text $ picotool help otp dump OTP DUMP: - Dump entire OTP + Dump entire OTP. (RP2350 only) SYNOPSIS: picotool otp dump [-r] [-e] [-p] [--output ] [device-selection] @@ -1271,7 +1271,7 @@ This command will list all the defined OTP rows & fields. You can list specific ```text $ picotool help otp list OTP LIST: - List matching known registers/fields + List matching known registers/fields. SYNOPSIS: picotool otp list [-p] [-n] [-f] [-i ] [..] @@ -1370,7 +1370,7 @@ The `bdev` commands are for interacting with block devices in Flash. The block d ```text $ picotool help bdev BDEV: - Commands related to embedded block devices + Commands related to embedded block devices. SYNOPSIS: picotool bdev ls [] [-r] [-p ] [--partition-name ] [-r] [-p ] [--partition-name [-p ] [--partition-name ] @@ -1485,7 +1485,7 @@ Copy file to/from the block device - use :filename to indicate files on the devi $ picotool help bdev cp BDEV CP: Copy file to/from the block device - use :filename to indicate files on the device (eg `cp - main.py :main.py` to upload to the device) + main.py :main.py` to upload to the device). SYNOPSIS: picotool bdev cp [-p ] [--partition-name ] @@ -1523,7 +1523,7 @@ Delete a file or an empty directory on the block device ```text $ picotool help bdev rm BDEV RM: - Delete a file or an empty directory on the block device + Delete a file or an empty directory on the block device. SYNOPSIS: picotool bdev rm [-p ] [--partition-name ] @@ -1559,7 +1559,7 @@ Print contents of file on the block device ```text $ picotool help bdev cat BDEV CAT: - Print contents of file on the block device + Print contents of file on the block device. SYNOPSIS: picotool bdev cat [-p ] [--partition-name ] @@ -1595,7 +1595,7 @@ Format the block device - may result in data loss ```text $ picotool help bdev format BDEV FORMAT: - Format the block device + Format the block device. SYNOPSIS: picotool bdev format [-p ] [--partition-name ] diff --git a/main.cpp b/main.cpp index d221fa35..8474478a 100644 --- a/main.cpp +++ b/main.cpp @@ -892,7 +892,7 @@ struct info_command : public cmd { string get_doc() const override { #if HAS_LIBUSB - return "Display information from the target device(s) or file.\nWithout any arguments, this will display basic information for all connected RP-series devices in BOOTSEL mode"; + return "Display information from the target device(s) or file.\nWithout any arguments, this will display basic information for all connected RP-series devices in BOOTSEL mode."; #else return "Display information from the target file."; #endif @@ -965,7 +965,7 @@ struct bdev_ls_command : public cmd { } string get_doc() const override { - return "List contents of the block device"; + return "List contents of the block device."; } }; @@ -982,7 +982,7 @@ struct bdev_mkdir_command : public cmd { } string get_doc() const override { - return "Create directory on the block device"; + return "Create directory on the block device."; } }; @@ -1000,7 +1000,7 @@ struct bdev_cp_command : public cmd { } string get_doc() const override { - return "Copy file to/from the block device - use :filename to indicate files on the device (eg `cp main.py :main.py` to upload to the device)"; + return "Copy file to/from the block device - use :filename to indicate files on the device (eg `cp main.py :main.py` to upload to the device)."; } }; @@ -1017,7 +1017,7 @@ struct bdev_rm_command : public cmd { } string get_doc() const override { - return "Delete a file or an empty directory on the block device"; + return "Delete a file or an empty directory on the block device."; } }; @@ -1034,7 +1034,7 @@ struct bdev_cat_command : public cmd { } string get_doc() const override { - return "Print contents of file on the block device"; + return "Print contents of file on the block device."; } }; @@ -1050,7 +1050,7 @@ struct bdev_format_command : public cmd { } string get_doc() const override { - return "Format the block device"; + return "Format the block device."; } }; @@ -1065,7 +1065,7 @@ vector> bdev_sub_commands { struct bdev_command : public multi_cmd { bdev_command() : multi_cmd("bdev", bdev_sub_commands) {} string get_doc() const override { - return "Commands related to embedded block devices"; + return "Commands related to embedded block devices."; } }; @@ -1349,7 +1349,7 @@ struct partition_create_command : public cmd { } string get_doc() const override { - return "Create a partition table from json"; + return "Create a partition table from json."; } }; @@ -1364,7 +1364,7 @@ vector> partition_sub_commands { struct partition_command : public multi_cmd { partition_command() : multi_cmd("partition", partition_sub_commands) {} string get_doc() const override { - return "Commands related to RP2350 Partition Tables"; + return "Commands related to RP2350 Partition Tables."; } }; @@ -1397,7 +1397,7 @@ struct otp_list_command : public cmd { } string get_doc() const override { - return "List matching known registers/fields"; + return "List matching known registers/fields."; } }; #if HAS_LIBUSB @@ -1436,7 +1436,7 @@ struct otp_get_command : public cmd { } string get_doc() const override { - return "Get the value of one or more OTP registers/fields"; + return "Get the value of one or more OTP registers/fields."; } }; @@ -1468,7 +1468,7 @@ struct otp_dump_command : public cmd { } string get_doc() const override { - return "Dump entire OTP"; + return "Dump entire OTP."; } }; @@ -1531,7 +1531,7 @@ struct otp_set_command : public cmd { } string get_doc() const override { - return "Set the value of an OTP row/field"; + return "Set the value of an OTP row/field."; } }; @@ -1555,7 +1555,7 @@ struct otp_permissions_command : public cmd { } string get_doc() const override { - return "Set the OTP access permissions"; + return "Set the OTP access permissions."; } }; @@ -1576,7 +1576,7 @@ struct otp_white_label_command : public cmd { } string get_doc() const override { - return "Set the white labelling values in OTP"; + return "Set the white labelling values in OTP."; } }; #endif @@ -1597,13 +1597,14 @@ vector> otp_sub_commands { struct otp_command : public multi_cmd { otp_command() : multi_cmd("otp", otp_sub_commands) {} string get_doc() const override { - return "Commands related to the RP2350 OTP (One-Time-Programmable) Memory"; + return "Commands related to the RP2350 OTP (One-Time-Programmable) Memory."; } }; #if HAS_LIBUSB struct uf2_info_command : public cmd { uf2_info_command() : cmd("info") {} + virtual bool requires_rp2350() const override { return true; } bool execute(device_map &devices) override; group get_cli() override { @@ -1704,7 +1705,7 @@ vector> uf2_sub_commands { struct uf2_command : public multi_cmd { uf2_command() : multi_cmd("uf2", uf2_sub_commands) {} string get_doc() const override { - return "Commands related to UF2 creation and status"; + return "Commands related to UF2 creation and status."; } }; @@ -1744,7 +1745,7 @@ struct help_command : public cmd { } string get_doc() const override { - return "Show general help, or help for a specific command or topic"; + return "Show general help, or help for a specific command or topic."; } }; @@ -1791,7 +1792,7 @@ struct version_command : public cmd { } string get_doc() const override { - return "Display picotool version"; + return "Display picotool version."; } }; @@ -1818,7 +1819,7 @@ struct reboot_command : public cmd { } string get_doc() const override { - return "Reboot the device"; + return "Reboot the device."; } }; auto reboot_cmd = std::shared_ptr(new reboot_command()); @@ -1937,7 +1938,11 @@ int parse(const int argc, char **argv) { if (help_mode && selected_cmd) { section_header(help_mode_prefix + selected_cmd->name()); fos.first_column(tab); - fos << selected_cmd->get_doc() << "\n"; + fos << selected_cmd->get_doc(); + if (selected_cmd->requires_rp2350()) { + fos << " (RP2350 only)"; + } + fos << "\n"; } else if (!selected_cmd && !no_global_header) { section_header(tool_name); fos.first_column(tab); From 01e5b66be16b7a4ba05bbdec33fa2e60864b9258 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 16 Jul 2026 19:07:23 +0100 Subject: [PATCH 10/15] Add "Use "picotool help uf2 " for more info" to end of `picotool help uf2` etc --- README.md | 4 ++++ main.cpp | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0aacba59..4af6266a 100644 --- a/README.md +++ b/README.md @@ -981,6 +981,8 @@ SUB COMMANDS: permissions Set the OTP access permissions. (RP2350 only) dump Dump entire OTP. (RP2350 only) list List matching known registers/fields. + +Use "picotool help otp " for more info ``` ### get/set @@ -1400,6 +1402,8 @@ SUB COMMANDS: rm Delete a file or an empty directory on the block device. cat Print contents of file on the block device. format Format the block device. + +Use "picotool help bdev " for more info ``` ### ls diff --git a/main.cpp b/main.cpp index 8474478a..6231a7fd 100644 --- a/main.cpp +++ b/main.cpp @@ -2070,11 +2070,15 @@ int parse(const int argc, char **argv) { } else { print_options_for(selected_cmd->get_cli()); } - if (!selected_cmd) { + if (!selected_cmd || selected_cmd->is_multi()) { fos.first_column(0); fos.hanging_indent(0); fos.wrap_hard(); - fos << "Use \"picotool help \" or \"picotool help \" for more info\n"; + if (selected_cmd) { + fos << "Use \"picotool help " << selected_cmd->name() << " \" for more info\n"; + } else { + fos << "Use \"picotool help \" or \"picotool help \" for more info\n"; + } #if !HAS_LIBUSB if (!help_mode) { fos << built_without_libusb_message; From b473059c7f9b5c2c55b0178fef2a88ebc8d93147 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 16 Jul 2026 19:15:29 +0100 Subject: [PATCH 11/15] Support --version, but print warning when it is used --- main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/main.cpp b/main.cpp index 6231a7fd..2a55c9a6 100644 --- a/main.cpp +++ b/main.cpp @@ -2090,6 +2090,12 @@ int parse(const int argc, char **argv) { auto args = cli::make_args(argc, argv); + // Check if the only argument is --version, if so replace it with version + if (args[0] == "--version" && args.size() == 1) { + printf("WARNING: Use \"picotool version\" instead of \"picotool --version\"\n"); + args[0] = "version"; + } + // Check if any -h or --help argument was requested, if so insert the help subcommand before any // arguments, this will ensure the correct help gets printed automatically. bool help_requested = std::find(args.begin(), args.end(), "--help") != args.end() From 226098de19a1ed8f0f00f15372a42b0fa2d4784a Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 16 Jul 2026 19:29:36 +0100 Subject: [PATCH 12/15] Add super-commands to the README --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ gen_help_txt.sh | 4 ++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4af6266a..4967d09d 100644 --- a/README.md +++ b/README.md @@ -695,6 +695,25 @@ OPTIONS: The `partition` commands allow you to interact with the partition tables on RP2350 devices, and also create them. +```text +$ picotool help partition +PARTITION: + Commands related to RP2350 Partition Tables. + +SYNOPSIS: + picotool partition info [-m ] [device-selection] + picotool partition create [--quiet] [--verbose] [-t ] [[-o + ] [--family ]] [] [-t ] [[--sign + ] [-t ] [--no-hash] [--singleton] [--no-btstack-flash-bank]] + [[--abs-block] []] + +SUB COMMANDS: + info Print the device's partition table. + create Create a partition table from json. + +Use "picotool help partition " for more info +``` + ### info ```text @@ -790,6 +809,28 @@ OPTIONS: The `uf2` commands allow for creation of UF2s, and can provide information if a UF2 download has failed. +```text +$ picotool help uf2 +UF2: + Commands related to UF2 creation and status. + +SYNOPSIS: + picotool uf2 convert [--quiet] [--verbose] [-t ] [-t ] [-o + ] [--family ] [--platform ] [[--abs-block] + []] + picotool uf2 combine [--quiet] [--verbose] [-t ] [-t ] + [-t ] [--family ] [--offset ] [--partition + ] [[--abs-block] []] + picotool uf2 info [device-selection] + +SUB COMMANDS: + convert Convert ELF/BIN to UF2. + combine Combine multiple UF2 files. + info Print info about UF2 download. (RP2350 only) + +Use "picotool help uf2 " for more info +``` + ### convert This command replaces the elf2uf2 functionality that was previously in the Raspberry Pi Pico SDK. It will attempt to auto-detect the family ID, but if this fails you can specify one manually with the `--family` argument. diff --git a/gen_help_txt.sh b/gen_help_txt.sh index 8b86554f..534e64a8 100755 --- a/gen_help_txt.sh +++ b/gen_help_txt.sh @@ -75,7 +75,7 @@ echo "Checking for missing help text sections..." missing_commands=() while IFS= read -r cmd; do if [ ! -z "$cmd" ]; then - if ! grep -q "\$ picotool help $cmd" tmp/README.md; then + if ! grep -qxF "\$ picotool help $cmd" tmp/README.md; then missing_commands+=("$cmd") echo "Missing help text section for command: $cmd" fi @@ -108,7 +108,7 @@ echo "Checking for missing help topic sections..." missing_topics=() while IFS= read -r topic; do if [ ! -z "$topic" ]; then - if ! grep -q "\$ picotool help $topic" tmp/README.md; then + if ! grep -qxF "\$ picotool help $topic" tmp/README.md; then missing_topics+=("$topic") echo "Missing help text section for topic: $topic" fi From 6279fc8e8ebadcc230b693ce9fa162830500f382 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 10 Sep 2026 13:36:14 +0100 Subject: [PATCH 13/15] review fixups --- README.md | 59 ++++++++++++++++++++++--------------------------------- cli.h | 12 ++++++++++- main.cpp | 37 +++++++++++++++++----------------- 3 files changed, 53 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 4967d09d..bccab81d 100644 --- a/README.md +++ b/README.md @@ -105,13 +105,13 @@ OPTIONS: The normal output is this format: ```text $ picotool version -picotool v2.3.0 (Linux, GNU-12.2.0, Release) +picotool v2.3.1 (Linux, GNU-12.2.0, Release) ``` When built without libusb you get an additional message: ```text $ picotool version -picotool v2.3.0 (Linux, GNU-12.2.0, Release) +picotool v2.3.1 (Linux, GNU-12.2.0, Release) This version of picotool was compiled without USB support. Some commands are not available. ``` @@ -119,20 +119,20 @@ This version of picotool was compiled without USB support. Some commands are not The semantic output is: ```text $ picotool version -s -2.3.0 +2.3.1 ``` -When checking compatibility with a compatible version (e.g. 2.2.0 with version 2.3.0): +When checking compatibility with a compatible version (e.g. 2.2.0 with picotool version 2.3.1): ```text -$ picotool version -s 2.2.0 -2.3.0 +$ picotool version 2.2.0 +picotool v2.3.1 (Linux, GNU-12.2.0, Release) ``` -Or with an incompatible version (e.g. 2.4.0 with version 2.3.0), it will exit with error code `ERROR_INCOMPATIBLE` (-3): +Or with an incompatible version (e.g. 2.3.1 with picotool version 2.2.0), it will exit with error code `ERROR_INCOMPATIBLE` (-3): ```text -$ picotool version -s 2.4.0 -2.3.0 -ERROR: Version 2.4.0 not compatible with this software +$ picotool version 2.3.1 +picotool v2.2.0 (Linux, GNU-12.2.0, Release) +ERROR: Version 2.3.1 not compatible with this software ``` @@ -375,13 +375,10 @@ OPTIONS: Save the installed program only. This is the default -a, --all Save all of flash memory - -r, --range - Save a range of memory. Note that UF2s always store complete 256 byte-aligned - blocks of 256 bytes, and the range is expanded accordingly - - The lower address bound in hex - - The upper address bound in hex + -r, --range + Save a range of memory (address bounds in hex). Note that UF2s always store + complete 256 byte-aligned blocks of 256 bytes, and the range is expanded + accordingly. Other -v, --verify Verify the data was saved correctly @@ -468,17 +465,11 @@ OPTIONS: Selection of data to erase -a, --all Erase all of flash memory. This is the default - -p, --partition + -p, --partition Erase a partition - - Partition number to erase - -r, --range - Erase a range of memory. Note that erases must be 4096 byte-aligned, so the range - is expanded accordingly - - The lower address bound in hex - - The upper address bound in hex + -r, --range + Erase a range of memory (address bounds in hex). Note that erases must be 4096 + byte-aligned, so the range is expanded accordingly. Target device selection See "picotool help device-selection" for available options ``` @@ -703,8 +694,8 @@ PARTITION: SYNOPSIS: picotool partition info [-m ] [device-selection] picotool partition create [--quiet] [--verbose] [-t ] [[-o - ] [--family ]] [] [-t ] [[--sign - ] [-t ] [--no-hash] [--singleton] [--no-btstack-flash-bank]] + ] [--family ]] [] [-t ] [[--sign + [-t ]] [--no-hash] [--singleton] [--no-btstack-flash-bank]] [[--abs-block] []] SUB COMMANDS: @@ -761,8 +752,8 @@ PARTITION CREATE: SYNOPSIS: picotool partition create [--quiet] [--verbose] [-t ] [[-o - ] [--family ]] [] [-t ] [[--sign - ] [-t ] [--no-hash] [--singleton] [--no-btstack-flash-bank]] + ] [--family ]] [] [-t ] [[--sign + [-t ]] [--no-hash] [--singleton] [--no-btstack-flash-bank]] [[--abs-block] []] OPTIONS: @@ -788,10 +779,8 @@ OPTIONS: -t, --type Specify file type (elf) explicitly, ignoring file extension Partition Table Options - --sign - The file name - -t, --type - Specify file type (pem) explicitly, ignoring file extension + --sign [-t, --type ] + Sign the partition table with a PEM key --no-hash Don't hash the partition table --singleton diff --git a/cli.h b/cli.h index 259be689..17461012 100644 --- a/cli.h +++ b/cli.h @@ -207,6 +207,10 @@ namespace cli { return _doc_non_optional; } + bool synopsis_non_optional() const { + return _synopsis_non_optional; + } + bool force_expand_help() const { return _force_expand_help; } @@ -237,6 +241,7 @@ namespace cli { int _min = 1; int _max = 1; bool _doc_non_optional = false; + bool _synopsis_non_optional = false; bool _force_expand_help = false; string _collapse_synopsys = ""; }; @@ -282,6 +287,11 @@ namespace cli { return *static_cast(this); } + D &synopsis_non_optional(bool v) { + _synopsis_non_optional = v; + return *static_cast(this); + } + D &force_expand_help(bool v) { _force_expand_help = v; return *static_cast(this); @@ -669,7 +679,7 @@ namespace cli { } static string decorate(const matchable &e, string s) { - if (e.is_optional() && !e.doc_non_optional()) { + if (e.is_optional() && !e.doc_non_optional() && !e.synopsis_non_optional()) { return string("[") + s + "]"; } else { return s; diff --git a/main.cpp b/main.cpp index 2a55c9a6..f8919ecd 100644 --- a/main.cpp +++ b/main.cpp @@ -838,6 +838,14 @@ std::map help_topics { named_file_types_x(types, i)\ ) +#define named_option_typed_file_selection_x(name, option, i, types)\ +(\ + option & value(name).with_exclusion_filter([](const string &value) {\ + return value.find_first_of('-') == 0;\ + }).set(settings.filenames[i]) % "The file name" +\ + named_file_types_x(types, i)\ +) + #define option_untyped_file_selection_x(option, i)\ (\ option & value("filename").with_exclusion_filter([](const string &value) {\ @@ -1101,11 +1109,9 @@ struct save_command : public cmd { ( option('p', "--program") % "Save the installed program only. This is the default" | option('a', "--all").doc_non_optional(true).set(settings.save.all) % "Save all of flash memory" | - ( - option('r', "--range").set(settings.range_set) % "Save a range of memory. Note that UF2s always store complete 256 byte-aligned blocks of 256 bytes, and the range is expanded accordingly" & - hex("from").set(settings.from) % "The lower address bound in hex" & - hex("to").set(settings.to) % "The upper address bound in hex" - ).min(0).doc_non_optional(true) + (option('r', "--range").synopsis_non_optional(true).set(settings.range_set) & + hex("from").synopsis_non_optional(true).set(settings.from) & + hex("to").synopsis_non_optional(true).set(settings.to)).synopsis_non_optional(true) % "Save a range of memory (address bounds in hex). Note that UF2s always store complete 256 byte-aligned blocks of 256 bytes, and the range is expanded accordingly." ).min(0).doc_non_optional(true).no_match_beats_error(false) % "Selection of data to save" + option('v', "--verify").set(settings.save.verify) % "Verify the data was saved correctly" + (option("--family") & @@ -1161,15 +1167,11 @@ struct erase_command : public cmd { return ( ( option('a', "--all") % "Erase all of flash memory. This is the default" | - ( - option('p', "--partition") % "Erase a partition" & - integer("partition").set(settings.load.partition) % "Partition number to erase" - ).min(0).doc_non_optional(true) | - ( - option('r', "--range").set(settings.range_set) % "Erase a range of memory. Note that erases must be 4096 byte-aligned, so the range is expanded accordingly" & - hex("from").set(settings.from) % "The lower address bound in hex" & - hex("to").set(settings.to) % "The upper address bound in hex" - ).min(0).doc_non_optional(true) + (option('p', "--partition").synopsis_non_optional(true) & + integer("partition").set(settings.load.partition)).synopsis_non_optional(true) % "Erase a partition" | + (option('r', "--range").synopsis_non_optional(true).set(settings.range_set) & + hex("from").synopsis_non_optional(true).set(settings.from) & + hex("to").synopsis_non_optional(true).set(settings.to)).synopsis_non_optional(true) % "Erase a range of memory (address bounds in hex). Note that erases must be 4096 byte-aligned, so the range is expanded accordingly." ).min(0).doc_non_optional(true).no_match_beats_error(false) % "Selection of data to erase" + ( // note this parenthesis seems to help with error messages for say erase --foo device_selection % "Target device selection" @@ -1330,11 +1332,8 @@ struct partition_create_command : public cmd { ( #if HAS_MBEDTLS // todo why doesn't this set settings.partition.sign? - ((option("--sign").set(settings.partition.sign) & value("keyfile").with_exclusion_filter([](const string &value) { - return value.find_first_of('-') == 0; - }).set(settings.filenames[3])) % "The file name" + - named_file_types_x("pem", 3)) % "Sign the partition table" + - (option("--no-hash").clear(settings.partition.hash) % "Don't hash the partition table") + + named_option_typed_file_selection_x("keyfile", option("--sign").set(settings.partition.sign), 3, "pem") % "Sign the partition table with a PEM key" + + (option("--no-hash").clear(settings.partition.hash) % "Don't hash the partition table") + #endif (option("--singleton").set(settings.partition.singleton) % "Singleton partition table") + (option("--no-btstack-flash-bank").set(settings.partition.no_btstack_flash_bank) % "Don't check for compatibility with BTStack flash bank") From 6d03e5a1f3a7b5368e9a7e167be2bc360080f22f Mon Sep 17 00:00:00 2001 From: will-v-pi <108662275+will-v-pi@users.noreply.github.com> Date: Thu, 10 Sep 2026 13:36:59 +0100 Subject: [PATCH 14/15] Update README.md Co-authored-by: Andrew Scheller --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bccab81d..6f97a851 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ COMMANDS: encrypt Encrypt the program. partition Commands related to RP2350 Partition Tables. uf2 Commands related to UF2 creation and status. - otp Commands related to the RP2350 OTP (One-Time-Programmable) Memory. + otp Commands related to the RP2350 OTP (One-Time-Programmable) memory. coprodis Post-process coprocessor instructions in disassembly files. link Link multiple binaries into one block loop. bdev Commands related to embedded block devices. From 6f23aec7cc4bd7989d0cb7406150c9c228c36895 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 10 Sep 2026 13:40:32 +0100 Subject: [PATCH 15/15] fix readme --- README.md | 4 ++-- main.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6f97a851..36463f87 100644 --- a/README.md +++ b/README.md @@ -773,7 +773,7 @@ OPTIONS: Specify the load address for UF2 file output (memory address; default 0x10000000) --family Specify the family id for UF2 file output (default absolute) - embed partition table into bootloader ELF + Embed partition table into bootloader ELF The file name -t, --type @@ -986,7 +986,7 @@ For the `list`, `set`, `get` and `load` commands, you can define your own OTP la ```text $ picotool help otp OTP: - Commands related to the RP2350 OTP (One-Time-Programmable) Memory. + Commands related to the RP2350 OTP (One-Time-Programmable) memory. SYNOPSIS: picotool otp get [-c ] [-r] [-e] [-n] [-i ] [device-selection] [-z] diff --git a/main.cpp b/main.cpp index f8919ecd..cd3ca9c7 100644 --- a/main.cpp +++ b/main.cpp @@ -1328,7 +1328,7 @@ struct partition_create_command : public cmd { (option("--family") & family_id("family_id").set(settings.family_id)) % "Specify the family id for UF2 file output (default absolute)" ).min(0).force_expand_help(true) % "UF2 output options") + - optional_typed_file_selection_x("bootloader", 2, "elf") % "embed partition table into bootloader ELF" + + optional_typed_file_selection_x("bootloader", 2, "elf") % "Embed partition table into bootloader ELF" + ( #if HAS_MBEDTLS // todo why doesn't this set settings.partition.sign? @@ -1596,7 +1596,7 @@ vector> otp_sub_commands { struct otp_command : public multi_cmd { otp_command() : multi_cmd("otp", otp_sub_commands) {} string get_doc() const override { - return "Commands related to the RP2350 OTP (One-Time-Programmable) Memory."; + return "Commands related to the RP2350 OTP (One-Time-Programmable) memory."; } };