You need to set PICO_SDK_PATH in the environment, or pass it to cmake with -DPICO_SDK_PATH=/path/to/pico-sdk
. To use features such as signing or hashing, you will need to make sure the mbedtls submodule in the SDK is checked out - this can be done by running this from your SDK directory.
git submodule update --init lib/mbedtls
You also need to install libusb-1.0
.
Use your favorite package tool to install dependencies. For example, on Ubuntu:
sudo apt install build-essential pkg-config libusb-1.0-0-dev cmake
Then simply build like a normal CMake project:
mkdir buildcd buildcmake ..make
On Linux you can add udev rules in order to run picotool without sudo:
sudo cp udev/99-picotool.rules /etc/udev/rules.d/
Download libUSB from here https://libusb.info/
set LIBUSB_ROOT environment variable to the install directory.
mkdir buildcd buildcmake -G "NMake Makefiles" ..nmake
Download libUSB from here https://libusb.info/
set LIBUSB_ROOT environment variable to the install directory.
mkdir buildcd buildcmake ..make
No need to download libusb separately or set LIBUSB_ROOT
.
pacman -S $MINGW_PACKAGE_PREFIX-{toolchain,cmake,libusb}mkdir buildcd buildcmake .. -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIXcmake --build .
The Raspberry Pi Pico SDK (pico-sdk) version 2.0.0 and above uses picotool
to do the ELF-to-UF2 conversion previously handled by the elf2uf2
tool in the SDK. The SDK also uses picotool
to hash and sign binaries.
Whilst the SDK can download picotool on its own per project, if you have multiple projects or build configurations, it is preferable to install a single copy of picotool
locally. This can be done most simply with make install
or cmake --install .
; the SDK will use this installed version by default.
Alternatively, you can install to a custom path via:
cmake -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR -DPICOTOOL_FLAT_INSTALL=1 ..
In order for the SDK to find picotool
in this custom path, you will need to set the picotool_DIR
variable in your project, either by setting the picotool_DIR
environment variable, by passing -Dpicotool_DIR=$MY_INSTALL_DIR/picotool
to your cmake
command, or by adding set(picotool_DIR $MY_INSTALL_DIR/picotool)
to your CMakeLists.txt file.
picotool
is a tool for working with RP2040/RP2350 binaries, and interacting with RP2040/RP2350 devices when they are in BOOTSEL mode. (As of version 1.1 of picotool
it is also possible to interact with devices that are not in BOOTSEL mode, but are using USB stdio support from the Raspberry Pi Pico SDK by using the -f
argument of picotool
).
Note for additional documentation see https://rptl.io/pico-get-started
$ picotool help PICOTOOL: Tool for interacting with RP2040/RP2350 device(s) in BOOTSEL mode, or with an RP2040/RP2350 binary SYNOPSIS: picotool info [-b] [-p] [-d] [--debug] [-l] [-a] [device-selection] picotool info [-b] [-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 [-p] [-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] [device-selection] picotool save -a [device-selection] picotool save -r <from> <to> [device-selection] picotool verify [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 picotool uf2 info|convert picotool version [-s] [<version>] picotool coprodis [--quiet] [--verbose] <infile> [-t <type>] <outfile> [-t <type>] picotool help [<cmd>] COMMANDS: info Display information from the target device(s) or file. Without any arguments, this will display basic information for all connected RP2040 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. encrypt Encrypt the program. seal Add final metadata to a binary, optionally including a hash and/or signature. link Link multiple binaries into one block loop. 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. reboot Reboot the device 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 version Display picotool version coprodis Post-process coprocessor instructions in dissassembly files. help Show general help or help for a specific command Use "picotool help <cmd>" for more info
Note commands that aren't acting on files require a device in BOOTSEL mode to be connected.
The is Binary Information support in the SDK which allows for easily storing compact information that picotool
can find (See Binary Info section below). The info command is for reading this information.
The information can be either read from one or more connected devices in BOOTSEL mode, or from a file. This file can be an ELF, a UF2 or a BIN file.
$ picotool help info INFO: Display information from the target device(s) or file. Without any arguments, this will display basic information for all connected RP2040 devices in BOOTSEL mode SYNOPSIS: picotool info [-b] [-p] [-d] [--debug] [-l] [-a] [device-selection] picotool info [-b] [-p] [-d] [--debug] [-l] [-a] <filename> [-t <type>] OPTIONS: Information to display -b, --basic Include basic information. This is the default -p, --pins Include pin information -d, --device Include device information --debug Include device debug information -l, --build Include build attributes -a, --all Include all information TARGET SELECTION: To target one or more connected RP2040 device(s) in BOOTSEL mode (the default) --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 RPI-RP2 drive mounted To target a file <filename> The file name -t <type> Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
Note the -f arguments vary slightly for Windows vs macOS / Unix platforms.
e.g.
$ picotool info Program Information name: hello_world features: stdout to UART
$ picotool info -a Program Information name: hello_world features: stdout to UART binary start: 0x10000000 binary end: 0x1000606c Fixed Pin Information 20: UART1 TX 21: UART1 RX Build Information build date: Dec 31 2020 build attributes: Debug build Device Information flash size: 2048K ROM version: 2
$ picotool info -bp Program Information name: hello_world features: stdout to UART
Fixed Pin Information 20: UART1 TX 21: UART1 RX
$ picotool info -a lcd_1602_i2c.uf2 File lcd_1602_i2c.uf2: Program Information name: lcd_1602_i2c web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c binary start: 0x10000000 binary end: 0x10003c1c Fixed Pin Information 4: I2C0 SDA 5: I2C0 SCL Build Information build date: Dec 31 2020
Config allows you to configure the binary info on a device, if it is configurable. Specifically, you can configure bi_ptr_int32
and bi_ptr_string
.
$ picotool help config
CONFIG:
Display or change program configuration settings from the target device(s) or file.
SYNOPSIS:
picotool config [-s <key> <value>] [-g <group>] [device-selection]
picotool config [-s <key> <value>] [-g <group>] <filename> [-t <type>]
OPTIONS:
<key>
Variable name
<value>
New value
-g <group>
Filter by feature group
TARGET SELECTION:
To target one or more connected RP2040 device(s) in BOOTSEL mode (the default)
--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
RPI-RP2 drive mounted
To target a file
<filename>
The file name
-t <type>
Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
$ picotool config
n = 5
name = "Billy"
nonconst_pins:
default_pin = 3
default_name = "My First Pin"
$ picotool config -g nonconst_pins
nonconst_pins:
default_pin = 3
default_name = "My First Pin"
$ picotool config -s name Jane
name = "Billy"
setting name -> "Jane"
$ picotool config
n = 5
name = "Jane"
nonconst_pins:
default_pin = 3
default_name = "My First Pin"
load
allows you to write data from a file onto the device (either writing to flash, or to RAM)
$ picotool help load
LOAD:
Load the program / memory range stored in a file onto the device.
SYNOPSIS:
picotool load [--ignore-partitions] [--family <family_id>] [-p <partition>] [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o
<offset>] [device-selection]
OPTIONS:
Post load actions
--ignore-partitions
When writing flash data, ignore the partition table and write to absolute space
--family
Specify the family ID of the file to load
<family_id>
family id to use for load
-p, --partition
Specify the partition to load into
<partition>
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
-N, --no-overwrite-unsafe
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
-u, --update
Skip writing flash sectors that already contain identical data
-v, --verify
Verify the data was written correctly
-x, --execute
Attempt to execute the downloaded file as a program after the load
File to load from
<filename>
The file name
-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
<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
RPI-RP2 drive mounted
e.g.
$ picotool load blink.uf2
Loading into Flash: [==============================] 100%
save
allows you to save a range of RAM, the program in flash, or an explicit range of flash from the device to a BIN file or a UF2 file.
$ picotool help save
SAVE:
Save the program / memory stored in flash on the device to a file.
SYNOPSIS:
picotool save [-p] [device-selection]
picotool save -a [device-selection]
picotool save -r <from> <to> [device-selection]
OPTIONS:
Selection of data to save
-p, --program
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
<from>
The lower address bound in hex
<to>
The upper address bound in hex
Source 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
RPI-RP2 drive mounted
File to save to
<filename>
The file name
-t <type>
Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
e.g. first looking at what is on the device...
$ picotool info
Program Information
name: lcd_1602_i2c
web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
... saving it to a file ...
$ picotool save spoon.uf2
Saving file: [==============================] 100%
Wrote 51200 bytes to spoon.uf2
... and looking at the file:
$ picotool info spoon.uf2
File spoon.uf2:
Program Information
name: lcd_1602_i2c
web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
seal
allows you to sign and/or hash a binary to run on RP2350.
By default, it will just sign the binary, but this can be configured with the --hash
and --no-sign
arguments.
Your signing key must be for the secp256k1 curve, in PEM format. You can create a .PEM file with:
openssl ecparam -name secp256k1 -genkey -out private.pem
$ picotool help seal
SEAL:
Add final metadata to a binary, optionally including a hash and/or signature.
SYNOPSIS:
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>..]]
OPTIONS:
--quiet
Don't print any output
--verbose
Print verbose output
--major <major>
Add Major Version
--minor <minor>
Add Minor Version
--rollback <rollback> [<rows>..]
Add Rollback Version
Configuration
--hash
Hash the file
--sign
Sign the file
--clear
Clear all of SRAM on load
File to load from
<infile>
The file name
-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
<offset>
Load offset (memory address; default 0x10000000)
File to save to
<outfile>
The file name
-t <type>
Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
Key file
<key>
The file name
-t <type>
Specify file type (pem) explicitly, ignoring file extension
File to save OTP to (will edit existing file if it exists)
<otp>
The file name
-t <type>
Specify file type (json) explicitly, ignoring file extension
encrypt
allows you to encrypt and sign a binary for use on the RP2350. By default, it will sign the encrypted binary, but that can be configured similarly to picotool sign
.
The encrypted binary will have the following structure:
First metadata block (5 words)
IV (4 words)
Encrypted Binary
Padding to ensure the encrypted length is a multiple of 4 words
Signature metadata block
The AES key must be provided as a .bin file of the 256 bit AES key to be used for encryption.
$ picotool help encrypt
ENCRYPT:
Encrypt the program.
SYNOPSIS:
picotool encrypt [--quiet] [--verbose] [--hash] [--sign] <infile> [-t <type>] [-o <offset>] <outfile> [-t <type>] <aes_key> [-t <type>]
[<signing_key>] [-t <type>]
OPTIONS:
--quiet
Don't print any output
--verbose
Print verbose output
Signing Configuration
--hash
Hash the encrypted file
--sign
Sign the encrypted file
File to load from
<infile>
The file name
-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
<offset>
Load offset (memory address; default 0x10000000)
File to save to
<outfile>
The file name
-t <type>
Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
AES Key
<aes_key>
The file name
-t <type>
Specify file type (bin) explicitly, ignoring file extension
Signing Key file
<signing_key>
The file name
-t <type>
Specify file type (pem) explicitly, ignoring file extension
The partition
commands allow you to interact with the partition tables on RP2350 devices, and also create them.
$ picotool help partition info
PARTITION INFO:
Print the device's partition table.
SYNOPSIS:
picotool partition info -m <family_id> [device-selection]
OPTIONS:
-m <family_id> [device-selection]
$ picotool partition info
un-partitioned_space : S(rw) NSBOOT(rw) NS(rw), uf2 { absolute }
partitions:
0(A) 00002000->00201000 S(rw) NSBOOT(rw) NS(rw), id=0000000000000000, "A", uf2 { rp2350-arm-s, rp2350-riscv }, arm_boot 1, riscv_boot 1
1(B w/ 0) 00201000->00400000 S(rw) NSBOOT(rw) NS(rw), id=0000000000000001, "B", uf2 { rp2350-arm-s, rp2350-riscv }, arm_boot 1, riscv_boot 1
$ picotool partition info -m rp2350-arm-s
un-partitioned_space : S(rw) NSBOOT(rw) NS(rw), uf2 { absolute }
partitions:
0(A) 00002000->00201000 S(rw) NSBOOT(rw) NS(rw), id=0000000000000000, "A", uf2 { rp2350-arm-s, rp2350-riscv }, arm_boot 1, riscv_boot 1
1(B w/ 0) 00201000->00400000 S(rw) NSBOOT(rw) NS(rw), id=0000000000000001, "B", uf2 { rp2350-arm-s, rp2350-riscv }, arm_boot 1, riscv_boot 1
Family id 'rp2350-arm-s' can be downloaded in partition 0:
00002000->00201000
This command allows you to create partition tables, and additionally embed them into the block loop if ELF files (for example, for bootloaders). By default, all partition tables are hashed, and you can also sign them.
$ picotool help partition create
PARTITION CREATE:
Create a partition table from json
SYNOPSIS:
picotool partition create [--quiet] [--verbose] <infile> [-t <type>] <outfile> [-t <type>] [[-o <offset>] [--family <family_id>]]
[<bootloader>] [-t <type>] [[--sign <keyfile>] [-t <type>] [--no-hash] [--singleton]] [[--abs-block] [<abs_block_loc>]]
OPTIONS:
--quiet
Don't print any output
--verbose
Print verbose output
partition table JSON
<infile>
The file name
-t <type>
Specify file type (json) explicitly, ignoring file extension
output file
<outfile>
The file name
-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
<offset>
Load offset (memory address; default 0x10000000)
--family
Specify the family if for UF2 file output
<family_id>
family id for UF2 (default absolute)
embed partition table into bootloader ELF
<bootloader>
The file name
-t <type>
Specify file type (elf) explicitly, ignoring file extension
Partition Table Options
--sign <keyfile>
The file name
-t <type>
Specify file type (pem) explicitly, ignoring file extension
--no-hash
Don't hash the partition table
--singleton
Singleton partition table
Errata RP2350-E9 Fix
--abs-block
Enforce support for an absolute block
<abs_block_loc>
absolute block location (default to 0x10ffff00)
The uf2
commands allow for creation of UF2s, and cam provide information when if a UF2 download has failed.
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.
picotool help uf2 convert
UF2 CONVERT:
Convert ELF/BIN to UF2.
SYNOPSIS:
picotool uf2 convert [--quiet] [--verbose] <infile> [-t <type>] <outfile> [-t <type>] [-o <offset>] [--family <family_id>]
[[--abs-block] [<abs_block_loc>]]
OPTIONS:
--quiet
Don't print any output
--verbose
Print verbose output
File to load from
<infile>
The file name
-t <type>
Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
File to save UF2 to
<outfile>
The file name
-t <type>
Specify file type (uf2) explicitly, ignoring file extension
Packaging Options
-o, --offset
Specify the load address
<offset>
Load offset (memory address; default 0x10000000 for BIN file)
UF2 Family options
<family_id>
family id for UF2
Errata RP2350-E9 Fix
--abs-block
Add an absolute block
<abs_block_loc>
absolute block location (default to 0x10ffff00)
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.
$ picotool help uf2 info
UF2 INFO:
Print info about UF2 download.
SYNOPSIS:
picotool uf2 info [device-selection]
OPTIONS:
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
RPI-RP2 drive mounted
The otp
commands are for interacting with the RP2350 OTP Memory. They are not available on RP2040 devices, as RP2040 has no OTP.
Note that the OTP Memory is One-Time-Programmable, which means that once a bit has been changed from 0 to 1, it cannot be changed back. Therefore, caution should be used when using these commands, as they risk bricking your RP2350 device. For example, if you set SECURE_BOOT_ENABLE but don't set a boot key, and disable the PICOBOOT interface, then your device will be unusable.
For the list
, set
, get
and load
commands, you can define your own OTP layout in a JSON file and pass that in with the -i
argument. These rows will be added to the default rows when parsing.
$ picotool help otp
OTP:
Commands related to the RP2350 OTP (One-Time-Programmable) Memory
SYNOPSIS:
picotool otp list [-p] [-n] [-i <filename>] [<selector>..]
picotool otp get [-c <copies>] [-r] [-e] [-n] [-i <filename>] [device-selection] [-z] [<selector>..]
picotool otp set [-c <copies>] [-r] [-e] [-i <filename>] [-z] <selector> <value> [device-selection]
picotool otp load [-r] [-e] [-s <row>] [-i <filename>] <filename> [-t <type>] [device-selection]
picotool otp dump [-r] [-e] [device-selection]
picotool otp permissions <filename> [-t <type>] [--led <pin>] [--hash] [--sign] [<key>] [-t <type>] [device-selection]
picotool otp white-label -s <row> <filename> [-t <type>] [device-selection]
SUB COMMANDS:
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)
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)
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
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 sign
.
For example, if you wish to sign a binary and then test secure boot with it, you can run the following set of commands:
$ picotool sign hello_world.elf hello_world.signed.elf private.pem otp.json
$ picotool load hello_world.signed.elf
$ picotool otp load otp.json
$ picotool reboot
This command allows for OTP white-labelling, which sets the USB configuration used by the device in BOOTSEL mode. This can be configured from a JSON file, an example of which is in sample-wl.json.
<cod