Add script to update README with output from help commands (#220)
This will update all the `picotool help ...` sections in the README with the current output
* Add workflow to check help text is up-to-date
* Add a check that all commands have help text in the readme
diff --git a/.github/workflows/check_help_text.yml b/.github/workflows/check_help_text.yml
new file mode 100644
index 0000000..99b859d
--- /dev/null
+++ b/.github/workflows/check_help_text.yml
@@ -0,0 +1,55 @@
+name: Check Help Text
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ check-help-text:
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install dependencies (Linux)
+ if: runner.os == 'Linux'
+ run: sudo apt install cmake ninja-build python3 build-essential libusb-1.0-0-dev
+
+ - name: Checkout Pico SDK
+ uses: actions/checkout@v4
+ with:
+ repository: raspberrypi/pico-sdk
+ ref: develop
+ path: pico-sdk
+ submodules: 'true'
+
+ - name: Build and Install
+ run: |
+ cmake -S . -B build -G "Ninja" -D PICO_SDK_PATH="${{ github.workspace }}/pico-sdk" -D GENERATE_FIXED_DOCS_WIDTH=1
+ cmake --build build
+ sudo cmake --install build
+
+ - name: Check if help text needs updating
+ run: |
+ # Create a copy of README.md
+ cp README.md README.md.orig
+
+ # Run the script
+ ./gen_help_txt.sh
+
+ # Compare the files
+ if ! cmp -s README.md README.md.orig; then
+ echo "Error: Help text in README.md is out of date - update by running gen_help_txt.sh, or use the artifact from this workflow"
+ exit 1
+ fi
+
+ # Clean up
+ rm README.md.orig
+
+ - name: Upload new README.md
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: README.md
+ path: README.md
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 94d674b..e9f14b4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -288,6 +288,10 @@
${LIBUSB_LIBRARIES})
endif()
+if (GENERATE_FIXED_DOCS_WIDTH)
+ target_compile_definitions(picotool PRIVATE DOCS_WIDTH=140)
+endif()
+
# allow `make install`
install(TARGETS picotool
EXPORT picotool-targets
diff --git a/README.md b/README.md
index b594878..6575310 100644
--- a/README.md
+++ b/README.md
@@ -14,17 +14,20 @@
picotool info [-b] [-m] [-p] [-d] [--debug] [-l] [-a] <filename> [-t <type>]
picotool config [-s <key> <value>] [-g <group>] [device-selection]
picotool config [-s <key> <value>] [-g <group>] <filename> [-t <type>]
- picotool load [--ignore-partitions] [--family <family_id>] [-p <partition>] [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o <offset>] [device-selection]
- picotool encrypt [--quiet] [--verbose] [--hash] [--sign] <infile> [-t <type>] [-o <offset>] <outfile> [-t <type>] <aes_key> [-t <type>] [<signing_key>] [-t <type>]
- picotool seal [--quiet] [--verbose] [--hash] [--sign] [--clear] <infile> [-t <type>] [-o <offset>] <outfile> [-t <type>] [<key>] [-t <type>] [<otp>] [-t <type>] [--major <major>] [--minor <minor>] [--rollback <rollback> [<rows>..]]
- picotool link [--quiet] [--verbose] <outfile> [-t <type>] <infile1> [-t <type>] <infile2> [-t <type>] [<infile3>] [-t <type>] [-p] <pad>
- picotool save [-p] [-v] [--family <family_id>] [device-selection]
- picotool save -a [-v] [--family <family_id>] [device-selection]
- picotool save -r <from> <to> [-v] [--family <family_id>] [device-selection]
+ picotool load [--ignore-partitions] [--family <family_id>] [-p <partition>] [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o
+ <offset>] [device-selection]
+ picotool encrypt [--quiet] [--verbose] [--hash] [--sign] <infile> [-t <type>] [-o <offset>] <outfile> [-t <type>] <aes_key> [-t <type>]
+ [<signing_key>] [-t <type>]
+ picotool seal [--quiet] [--verbose] [--hash] [--sign] [--clear] <infile> [-t <type>] [-o <offset>] <outfile> [-t <type>] [<key>] [-t
+ <type>] [<otp>] [-t <type>] [--major <major>] [--minor <minor>] [--rollback <rollback> [<rows>..]]
+ picotool link [--quiet] [--verbose] <outfile> [-t <type>] <infile1> [-t <type>] <infile2> [-t <type>] [<infile3>] [-t <type>] [-p <pad>]
+ picotool save [-p] [-v] [--family <family_id>] <filename> [-t <type>] [device-selection]
+ picotool save -a [-v] [--family <family_id>] <filename> [-t <type>] [device-selection]
+ picotool save -r <from> <to> [-v] [--family <family_id>] <filename> [-t <type>] [device-selection]
picotool erase [-a] [device-selection]
picotool erase -p <partition> [device-selection]
picotool erase -r <from> <to> [device-selection]
- picotool verify [device-selection]
+ picotool verify <filename> [-t <type>] [device-selection] [-r <from> <to>] [-o <offset>] [device-selection]
picotool reboot [-a] [-u] [-g <partition>] [-c <cpu>] [device-selection]
picotool otp list|get|set|load|dump|permissions|white-label
picotool partition info|create
@@ -116,7 +119,7 @@
-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
- RPI-RP2 drive mounted
+ USB drive mounted
To target a file
<filename>
The file name
@@ -224,7 +227,7 @@
-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
- RPI-RP2 drive mounted
+ USB drive mounted
To target a file
<filename>
The file name
@@ -325,7 +328,7 @@
-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
- RPI-RP2 drive mounted
+ USB drive mounted
```
e.g.
@@ -345,9 +348,9 @@
Save the program / memory stored in flash on the device to a file.
SYNOPSIS:
- picotool save [-p] [-v] [--family <family_id>] [device-selection]
- picotool save -a [-v] [--family <family_id>] [device-selection]
- picotool save -r <from> <to> [-v] [--family <family_id>] [device-selection]
+ picotool save [-p] [-v] [--family <family_id>] <filename> [-t <type>] [device-selection]
+ picotool save -a [-v] [--family <family_id>] <filename> [-t <type>] [device-selection]
+ picotool save -r <from> <to> [-v] [--family <family_id>] <filename> [-t <type>] [device-selection]
OPTIONS:
Selection of data to save
@@ -368,7 +371,12 @@
--family
Specify the family ID to save the file as
<family_id>
- family id to save file as
+ family ID to save file as
+ File to save to
+ <filename>
+ The file name
+ -t <type>
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
Source device selection
--bus <bus>
Filter devices by USB bus number
@@ -386,12 +394,7 @@
-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
- RPI-RP2 drive mounted
- File to save to
- <filename>
- The file name
- -t <type>
- Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ USB drive mounted
```
e.g. first looking at what is on the device...
@@ -419,6 +422,55 @@
web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
```
+## verify
+
+This command allows you to verify that the contents of Flash/SRAM on the device matches a file, similar to the `--verify` option for `save` and `load`.
+
+```text
+$ picotool help verify
+VERIFY:
+ Check that the device contents match those in the file.
+
+SYNOPSIS:
+ picotool verify <filename> [-t <type>] [-r <from> <to>] [-o <offset>] [device-selection]
+
+OPTIONS:
+ The file to compare against
+ <filename>
+ The file name
+ -t <type>
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ Address options
+ -r, --range
+ Compare a sub range of memory only
+ <from>
+ The lower address bound in hex
+ <to>
+ The upper address bound in hex
+ -o, --offset
+ Specify the load address when comparing with a BIN file
+ <offset>
+ Load offset (memory address; default 0x10000000)
+ Target device selection
+ --bus <bus>
+ Filter devices by USB bus number
+ --address <addr>
+ Filter devices by USB device address
+ --vid <vid>
+ Filter by vendor id
+ --pid <pid>
+ Filter by product id
+ --ser <ser>
+ Filter by serial number
+ -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
+```
+
## erase
`erase` allows you to erase all of flash, a partition of flash, or an explicit range of flash on the device.
@@ -431,7 +483,7 @@
SYNOPSIS:
picotool erase [-a] [device-selection]
- picotool erase [-p <partition>] [device-selection]
+ picotool erase -p <partition> [device-selection]
picotool erase -r <from> <to> [device-selection]
OPTIONS:
@@ -465,7 +517,7 @@
-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
- RPI-RP2 drive mounted
+ USB drive mounted
```
e.g. first looking at what is on the device...
@@ -646,10 +698,29 @@
Print the device's partition table.
SYNOPSIS:
- picotool partition info -m <family_id> [device-selection]
+ picotool partition info [-m <family_id>] [device-selection]
OPTIONS:
- -m <family_id> [device-selection]
+ -m <family_id>
+ family ID (will show target partition for said family)
+ Target device selection
+ --bus <bus>
+ Filter devices by USB bus number
+ --address <addr>
+ Filter devices by USB device address
+ --vid <vid>
+ Filter by vendor id
+ --pid <pid>
+ Filter by product id
+ --ser <ser>
+ Filter by serial number
+ -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
```
```text
@@ -738,7 +809,7 @@
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.
```text
-picotool help uf2 convert
+$ picotool help uf2 convert
UF2 CONVERT:
Convert ELF/BIN to UF2.
@@ -806,8 +877,7 @@
-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
- RPI-RP2 drive mounted
-
+ USB drive mounted
```
## otp
@@ -837,20 +907,184 @@
list List matching known registers/fields
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. (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)
dump Dump entire OTP (RP2350 only)
permissions Set the OTP access permissions (RP2350 only)
white-label Set the white labelling values in OTP (RP2350 only)
-
```
### set/get
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
+```text
+$ picotool help otp get
+OTP GET:
+ Get the value of one or more OTP registers/fields
+
+SYNOPSIS:
+ picotool otp get [-c <copies>] [-r] [-e] [-n] [-i <filename>] [device-selection] [-z] [<selector>..]
+
+OPTIONS:
+ Row/field options
+ -c <copies>
+ Read multiple redundant values
+ -r, --raw
+ Get raw 24-bit values
+ -e, --ecc
+ Use error correction
+ -n, --no-descriptions
+ Don't show descriptions
+ -i <filename>
+ Include extra otp definition
+ Row/Field Selection
+ -z, --fuzzy
+ Allow fuzzy name searches in selector vs exact match
+ <selector>
+ The row/field selector, each of which can select a whole row:
+
+ ROW_NAME to select a whole row by name.
+ ROW_NUMBER to select a whole row by number.
+ PAGE:PAGE_ROW_NUMBER to select a whole row by page and number within page.
+
+ ... or can select a single field/subset of a row (where ROW_SEL is one of the above row selectors):
+
+ ROW_SEL.FIELD_NAME to select a field within a row by name.
+ ROW_SEL.n-m to select a range of bits within a row.
+ ROW_SEL.n to select a single bit within a row.
+ .FIELD_NAME to select any row's field by name.
+
+ .. or can select multiple rows by using blank or '*' for PAGE or PAGE_ROW_NUMBER
+
+TARGET SELECTION:
+ Target device selection
+ --bus <bus>
+ Filter devices by USB bus number
+ --address <addr>
+ Filter devices by USB device address
+ --vid <vid>
+ Filter by vendor id
+ --pid <pid>
+ Filter by product id
+ --ser <ser>
+ Filter by serial number
+ -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
+```
+
+```text
+$ picotool help otp set
+OTP SET:
+ Set the value of an OTP row/field
+
+SYNOPSIS:
+ picotool otp set [-c <copies>] [-r] [-e] [-s] [-i <filename>] [-z] <selector> <value> [device-selection]
+
+OPTIONS:
+ Redundancy/Error Correction Overrides
+ -c <copies>
+ Write multiple redundant values
+ -r, --raw
+ Set raw 24-bit values
+ -e, --ecc
+ Use error correction
+ -s, --set-bits
+ Set bits only
+ -i <filename>
+ Include extra otp definition
+ <value>
+ The value to set
+ Row/Field Selection
+ -z, --fuzzy
+ Allow fuzzy name searches in selector vs exact match
+ <selector>
+ The row/field selector, which can select a whole row:
+
+ ROW_NAME to select a whole row by name.
+ ROW_NUMBER to select a whole row by number.
+ PAGE:PAGE_ROW_NUMBER to select a whole row by page and number within page.
+
+ ... or can select a single field/subset of a row (where ROW_SEL is one of the above row selectors):
+
+ ROW_SEL.FIELD_NAME to select a field within a row by name.
+ ROW_SEL.n-m to select a range of bits within a row.
+ ROW_SEL.n to select a single bit within a row.
+ .FIELD_NAME to select any row's field by name.
+
+TARGET SELECTION:
+ Target device selection
+ --bus <bus>
+ Filter devices by USB bus number
+ --address <addr>
+ Filter devices by USB device address
+ --vid <vid>
+ Filter by vendor id
+ --pid <pid>
+ Filter by product id
+ --ser <ser>
+ Filter by serial number
+ -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
+```
+
### load
This command allows loading of a range of OTP rows onto the device. The source can be a binary file, or a JSON file such as the one output by `picotool seal`. The schema for this JSON file is [here](json/schemas/otp-schema.json)
+
+```text
+$ 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).
+
+SYNOPSIS:
+ picotool otp load [-r] [-e] [-s <row>] [-i <filename>] <filename> [-t <type>] [device-selection]
+
+OPTIONS:
+ Row options
+ -r, --raw
+ Set raw 24-bit values. This is the default for BIN files
+ -e, --ecc
+ Use error correction
+ -s <row>
+ Start row to load at (note use 0x for hex)
+ -i <filename>
+ Include extra otp definition
+ File to load row(s) from
+ <filename>
+ The file name
+ -t <type>
+ Specify file type (json | bin) explicitly, ignoring file extension
+ Target device selection
+ --bus <bus>
+ Filter devices by USB bus number
+ --address <addr>
+ Filter devices by USB device address
+ --vid <vid>
+ Filter by vendor id
+ --pid <pid>
+ Filter by product id
+ --ser <ser>
+ Filter by serial number
+ -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
+```
+
For example, if you wish to sign a binary and then test secure boot with it, you can run the following set of commands:
```text
$ picotool seal --sign hello_world.elf hello_world.signed.elf private.pem otp.json
@@ -895,8 +1129,7 @@
-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
- RPI-RP2 drive mounted
-
+ USB drive mounted
```
```text
@@ -987,7 +1220,7 @@
-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
- RPI-RP2 drive mounted
+ USB drive mounted
```
```text
@@ -1010,6 +1243,149 @@
The device was rebooted to start the application.
```
+### dump
+
+This command will read the entire OTP contents and print it out as hex.
+
+```text
+$ picotool help otp dump
+OTP DUMP:
+ Dump entire OTP
+
+SYNOPSIS:
+ picotool otp dump [-r] [-e] [device-selection]
+
+OPTIONS:
+ Row/field options
+ -r, --raw
+ Get raw 24-bit values. This is the default
+ -e, --ecc
+ Use error correction
+
+TARGET SELECTION:
+ Target device selection
+ --bus <bus>
+ Filter devices by USB bus number
+ --address <addr>
+ Filter devices by USB device address
+ --vid <vid>
+ Filter by vendor id
+ --pid <pid>
+ Filter by product id
+ --ser <ser>
+ Filter by serial number
+ -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
+```
+
+### list
+
+This command will list all the defined OTP rows & fields. You can list specific rows/fields using the selector
+
+```text
+$ picotool help otp list
+OTP LIST:
+ List matching known registers/fields
+
+SYNOPSIS:
+ picotool otp list [-p] [-n] [-f] [-i <filename>] [<selector>..]
+
+OPTIONS:
+ Row/Field Selection
+ -p, --pages
+ Show page number/page row number
+ -n, --no-descriptions
+ Don't show descriptions
+ -f, --field-descriptions
+ Show all field descriptions
+ -i <filename>
+ Include extra otp definition
+ <selector>
+ The row/field selector, each of which can select a whole row:
+
+ ROW_NAME to select a whole row by name.
+ ROW_NUMBER to select a whole row by number.
+ PAGE:PAGE_ROW_NUMBER to select a whole row by page and number within page.
+
+ ... or can select a single field/subset of a row (where ROW_SEL is one of the above row selectors):
+
+ ROW_SEL.FIELD_NAME to select a field within a row by name.
+ ROW_SEL.n-m to select a range of bits within a row.
+ ROW_SEL.n to select a single bit within a row.
+ .FIELD_NAME to select any row's field by name.
+
+ .. or can select multiple rows by using blank or '*' for PAGE or PAGE_ROW_NUMBER
+```
+
+## coprodis
+
+This command is used by the SDK to post-process dissassembly files, to replace the coprocessor instructions (mcr, mrc, etc.) with more human readable ones (eg rcp_bvalid, gpioc_hi_put, etc.).
+
+```text
+$ picotool help coprodis
+COPRODIS:
+ Post-process coprocessor instructions in disassembly files.
+
+SYNOPSIS:
+ picotool coprodis [--quiet] [--verbose] <infile> [-t <type>] <outfile> [-t <type>]
+
+OPTIONS:
+ --quiet
+ Don't print any output
+ --verbose
+ Print verbose output
+ Input DIS
+ <infile>
+ The file name
+ -t <type>
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ Output DIS
+ <outfile>
+ The file name
+ -t <type>
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+```
+
+## link
+
+This command is used to link multiple binaries with block loops into a single larger block loop. It can currently link up to 3 files into a block loop. It will add the required Rolling Window Delta items to the new block loop, to ensure that everyting is rolled correctly when being executed. For examples of its usage, see the universal examples in [pico-examples](https://github.com/raspberrypi/pico-examples?tab=readme-ov-file#universal).
+
+```text
+$ picotool help link
+LINK:
+ Link multiple binaries into one block loop.
+
+SYNOPSIS:
+ picotool link [--quiet] [--verbose] <outfile> [-t <type>] <infile1> [-t <type>] <infile2> [-t <type>] [<infile3>] [-t <type>] [-p <pad>]
+
+OPTIONS:
+ --quiet
+ Don't print any output
+ --verbose
+ Print verbose output
+ -p <pad>
+ Specify alignment to pad to, defaults to 0x1000
+ File to write to
+ <outfile>
+ The file name
+ -t <type>
+ Specify file type (uf2 | bin) explicitly, ignoring file extension
+ Files to link
+ <infile1>
+ The file name
+ -t <type>
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ <infile2>
+ The file name
+ <infile3>
+ The file name
+```
+
## 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
new file mode 100755
index 0000000..c68efb3
--- /dev/null
+++ b/gen_help_txt.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+
+ALLOWED_MISSING_COMMANDS="version"
+
+mkdir -p tmp
+cp README.md tmp/README.md
+
+# Update each help text section with the current text
+while IFS= read -r line; do
+ if [[ $line =~ ^\$[[:space:]]*picotool[[:space:]]+help ]]; then
+ # Extract the commands
+ cmd=$(echo "$line" | sed 's/^\$[[:space:]]*//')
+ if [ ! -z "$cmd" ]; then
+ echo "Running: $cmd"
+ $cmd > "tmp/${cmd// /_}.txt"
+
+ # Create the new text section
+ {
+ printf '```text\n%s\n' "$line"
+ cat "tmp/${cmd// /_}.txt"
+ printf '```'
+ } > "tmp/new_section.txt"
+
+ # 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`; }
+ s/```text\n'"$escaped_line"'\n.*?\n```/$new/s;
+ ' tmp/README.md
+ fi
+ fi
+done < README.md
+
+
+# Extract commands to check for missing help text
+echo "Extracting commands from main.cpp..."
+
+extract_commands() {
+ local array_name=$1
+ local prefix=""
+ if [[ $array_name == *"_sub_commands" ]]; then
+ prefix=${array_name%_sub_commands}
+ fi
+ sed -n "/vector<std::shared_ptr<cmd>> $array_name/,/};/p" main.cpp | grep -o 'new \([a-z0-9_]*\)_command' | cut -d' ' -f2 | sed 's/_command$//' | while read cmd; do
+ if [[ ! -z "$prefix" ]]; then
+ # Remove prefix from command and replace remaining underscores with dashes
+ local cmd_without_prefix=${cmd#${prefix}_}
+ echo "${prefix} ${cmd_without_prefix//_/-}"
+ else
+ # Replace underscores with dashes for main commands
+ echo "${cmd//_/-}"
+ fi
+ done
+}
+
+# Extract top level commands
+main_commands=$(extract_commands "commands")
+
+# Extract sub-commands
+sub_command_arrays=$(grep -Eo 'vector<std::shared_ptr<cmd>> [a-z0-9_]+_sub_commands' main.cpp | cut -d' ' -f2)
+sub_commands=""
+for array in $sub_command_arrays; do
+ echo "Extracting commands from $array..."
+ array_commands=$(extract_commands "$array")
+ sub_commands+=$'\n'"$array_commands"
+done
+
+# Combine all commands
+commands=$(echo -e "$main_commands$sub_commands")
+
+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 [[ ! "$ALLOWED_MISSING_COMMANDS" =~ "$cmd" ]]; then
+ missing_commands+=("$cmd")
+ echo "Missing help text section for command: $cmd"
+ fi
+ fi
+ fi
+done <<< "$commands"
+
+# If there are missing commands, add their help text sections to the end of the file
+# Still fails the job later, this is just to provide the output to copy into the right place
+if [ ${#missing_commands[@]} -ne 0 ]; then
+ echo "Adding missing help text sections to end of file..."
+ for cmd in "${missing_commands[@]}"; do
+ echo "Running: picotool help $cmd"
+ picotool help $cmd > "tmp/picotool_help_${cmd// /_}.txt"
+
+ {
+ printf '\n```text\n$ picotool help %s\n' "$cmd"
+ cat "tmp/picotool_help_${cmd// /_}.txt"
+ printf '```\n'
+ } >> tmp/README.md
+ done
+fi
+
+mv tmp/README.md README.md
+rm -rf tmp
+
+echo "Help text sections have been updated in README.md"
+
+# Still fail if there are missing commands
+if [ ${#missing_commands[@]} -ne 0 ]; then
+ echo "Failing job due to missing help text sections"
+ exit 1
+fi
diff --git a/main.cpp b/main.cpp
index 8b1576b..7b7fd48 100644
--- a/main.cpp
+++ b/main.cpp
@@ -524,7 +524,7 @@
(option("--pid") & integer("pid").set(settings.pid)) % "Filter by product id" +
(option("--ser") & value("ser").set(settings.ser)) % "Filter by serial number"
+ option('f', "--force").set(settings.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" +
- option('F', "--force-no-reboot").set(settings.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 RPI-RP2 drive mounted"
+ option('F', "--force-no-reboot").set(settings.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"
).min(0).doc_non_optional(true).collapse_synopsys("device-selection");
#define file_types_x(i)\
@@ -668,7 +668,6 @@
group get_cli() override {
return (
- device_selection % "Target device selection" +
file_selection % "The file to compare against" +
(
(option('r', "--range").set(settings.range_set) % "Compare a sub range of memory only" &
@@ -676,7 +675,8 @@
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)
- ).min(0).doc_non_optional(true) % "Address options"
+ ).min(0).doc_non_optional(true) % "Address options" +
+ device_selection % "Target device selection"
);
}
@@ -704,8 +704,8 @@
(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) +
( // note this parenthesis seems to help with error messages for say save --foo
- device_selection % "Source device selection" +
- file_selection % "File to save to"
+ file_selection % "File to save to" +
+ device_selection % "Source device selection"
)
);
}
@@ -862,7 +862,7 @@
named_file_selection_x("infile1", 1) % "Files to link" +
named_file_selection_x("infile2", 2) % "Files to link" +
optional_file_selection_x("infile3", 3) % "Files to link" +
- option('p', "--pad") & hex("pad").set(settings.link.align) % "Specify alignment to pad to, defaults to 0x1000"
+ (option('p', "--pad") & hex("pad").set(settings.link.align)) % "Specify alignment to pad to, defaults to 0x1000"
);
}
@@ -965,12 +965,12 @@
"ROW_NAME to select a whole row by name.\n" \
"ROW_NUMBER to select a whole row by number.\n" \
"PAGE:PAGE_ROW_NUMBER to select a whole row by page and number within page.\n\n" \
- "... or can select a single field/subset of a row (where REG_SEL is one of the above row selectors):\n\n"
- "REG_SEL.FIELD_NAME to select a field within a row by name.\n" \
- "REG_SEL.n-m to select a range of bits within a row.\n" \
- "REG_SEL.n to select a single bit within a row.\n" \
+ "... or can select a single field/subset of a row (where ROW_SEL is one of the above row selectors):\n\n"
+ "ROW_SEL.FIELD_NAME to select a field within a row by name.\n" \
+ "ROW_SEL.n-m to select a range of bits within a row.\n" \
+ "ROW_SEL.n to select a single bit within a row.\n" \
".FIELD_NAME to select any row's field by name.\n\n" \
- ".. or can selected multiple rows by using blank or '*' for PAGE or PAGE_ROW_NUMBER").repeatable().min(0)
+ ".. or can select multiple rows by using blank or '*' for PAGE or PAGE_ROW_NUMBER").repeatable().min(0)
) % "Row/Field Selection"
);
}
@@ -989,7 +989,7 @@
return (
(
(option('c', "--copies") & integer("copies").min(1).set(settings.otp.redundancy)) % "Read multiple redundant values" +
- option('r', "--raw").set(settings.otp.raw) % "Get raw 24 bit values" +
+ option('r', "--raw").set(settings.otp.raw) % "Get raw 24-bit values" +
option('e', "--ecc").set(settings.otp.ecc) % "Use error correction" +
option('n', "--no-descriptions").set(settings.otp.list_no_descriptions) % "Don't show descriptions" +
(option('i', "--include") & value("filename").add_to(settings.otp.extra_files)).min(0).max(1) % "Include extra otp definition" // todo more than 1
@@ -1004,12 +1004,12 @@
"ROW_NAME to select a whole row by name.\n" \
"ROW_NUMBER to select a whole row by number.\n" \
"PAGE:PAGE_ROW_NUMBER to select a whole row by page and number within page.\n\n" \
- "... or can select a single field/subset of a row (where REG_SEL is one of the above row selectors):\n\n"
- "REG_SEL.FIELD_NAME to select a field within a row by name.\n" \
- "REG_SEL.n-m to select a range of bits within a row.\n" \
- "REG_SEL.n to select a single bit within a row.\n" \
+ "... or can select a single field/subset of a row (where ROW_SEL is one of the above row selectors):\n\n"
+ "ROW_SEL.FIELD_NAME to select a field within a row by name.\n" \
+ "ROW_SEL.n-m to select a range of bits within a row.\n" \
+ "ROW_SEL.n to select a single bit within a row.\n" \
".FIELD_NAME to select any row's field by name.\n\n" \
- ".. or can selected multiple rows by using blank or '*' for PAGE or PAGE_ROW_NUMBER").repeatable().min(0)
+ ".. or can select multiple rows by using blank or '*' for PAGE or PAGE_ROW_NUMBER").repeatable().min(0)
) % "Row/Field Selection"
);
}
@@ -1028,7 +1028,7 @@
group get_cli() override {
return (
(
- option('r', "--raw").set(settings.otp.raw) % "Get raw 24 bit values" +
+ option('r', "--raw").set(settings.otp.raw) % "Get raw 24-bit values. This is the default" +
option('e', "--ecc").set(settings.otp.ecc) % "Use error correction"
).min(0).doc_non_optional(true) % "Row/field options" +
(
@@ -1050,7 +1050,7 @@
group get_cli() override {
return (
(
- option('r', "--raw").set(settings.otp.raw) % "Get raw 24 bit values" +
+ option('r', "--raw").set(settings.otp.raw) % "Set raw 24-bit values. This is the default for BIN files" +
option('e', "--ecc").set(settings.otp.ecc) % "Use error correction" +
(option('s', "--start_row") & integer("row").set(settings.otp.row)) % "Start row to load at (note use 0x for hex)" +
(option('i', "--include") & value("filename").add_to(settings.otp.extra_files)).min(0).max(1) % "Include extra otp definition" // todo more than 1
@@ -1061,7 +1061,7 @@
}
string get_doc() const override {
- return "Load the row range stored in a file into OTP and verify. Data is 2 bytes/row for ECC, 4 bytes/row for raw.";
+ return "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).";
}
};
@@ -1074,8 +1074,8 @@
group get_cli() override {
return (
(
- (option('c', "--copies") & integer("copies").min(1).set(settings.otp.redundancy)) % "Read multiple redundant values" +
- option('r', "--raw").set(settings.otp.raw) % "Set raw 24 bit values" +
+ (option('c', "--copies") & integer("copies").min(1).set(settings.otp.redundancy)) % "Write multiple redundant values" +
+ option('r', "--raw").set(settings.otp.raw) % "Set raw 24-bit values" +
option('e', "--ecc").set(settings.otp.ecc) % "Use error correction" +
option('s', "--set-bits").set(settings.otp.ignore_set) % "Set bits only" +
(option('i', "--include") & value("filename").add_to(settings.otp.extra_files)).min(0).max(1) % "Include extra otp definition" // todo more than 1
@@ -1083,10 +1083,15 @@
(
option('z', "--fuzzy").set(settings.otp.fuzzy) % "Allow fuzzy name searches in selector vs exact match" +
(value("selector").add_to(settings.otp.selectors) %
- "The row/field selector, which can be:\nROW_NAME or ROW_NUMBER or PAGE:PAGE_ROW_NUMBER to select a whole row.\n"
- "FIELD, REG.FIELD, REG.n-m, PAGE:PAGE_ROW_NUMBER.FIELD or PAGE:PAGE_ROW_NUMBER.n-m to select a row field.\n\n"
- "where:\n\nREG and FIELD are names (or parts of names with fuzzy searches).\nPAGE and PAGE_ROW_NUMBER are page numbers and row within a page, "
- "ROW_NUMBER is an absolute row number offset, and n-m are the inclusive bit ranges of a field.")
+ "The row/field selector, which can select a whole row:\n\n" \
+ "ROW_NAME to select a whole row by name.\n" \
+ "ROW_NUMBER to select a whole row by number.\n" \
+ "PAGE:PAGE_ROW_NUMBER to select a whole row by page and number within page.\n\n" \
+ "... or can select a single field/subset of a row (where ROW_SEL is one of the above row selectors):\n\n"
+ "ROW_SEL.FIELD_NAME to select a field within a row by name.\n" \
+ "ROW_SEL.n-m to select a range of bits within a row.\n" \
+ "ROW_SEL.n to select a single bit within a row.\n" \
+ ".FIELD_NAME to select any row's field by name.")
) % "Row/Field Selection" +
integer("value").set(settings.otp.value) % "The value to set" +
(
@@ -7882,7 +7887,10 @@
}
void get_terminal_size(int& width, int& height) {
-#if defined(_WIN32)
+#if defined(DOCS_WIDTH)
+ width = DOCS_WIDTH;
+ height = 24;
+#elif defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
width = (int)(csbi.dwSize.X);