| ## Building |
| |
| You need to set PICO_SDK_PATH in the environment, or pass it to cmake. |
| |
| You also need to install `libusb-1.0`. |
| |
| Linux/Mac: use your favorite package tool. For example, on Ubuntu: |
| |
| ```console |
| sudo apt install build-essential pkg-config libusb-1.0-0-dev |
| ``` |
| |
| Windows: download from here https://libusb.info/ |
| |
| If you are on Windows, set LIBUSB_ROOT environment variable to the install directory |
| |
| ```console |
| mkdir build |
| cd build |
| cmake .. |
| make |
| ``` |
| |
| for Windows non MinGW/WSL: |
| |
| ```console |
| mkdir build |
| cd build |
| cmake -G "NMake Makefiles" .. |
| nmake |
| ``` |
| ## Overview |
| |
| Picotool is a tool for inspecting RP2040 binaries, and interacting with RP2040 devices when they are in BOOTSEL mode. |
| |
| Note for full documentation see https://rptl.io/pico-get-started |
| |
| ```text |
| $ picotool help |
| PICOTOOL: |
| Tool for interacting with a RP2040 device in BOOTSEL mode, or with a RP2040 binary |
| |
| SYNOPSYS: |
| picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] |
| picotool info [-b] [-p] [-d] [-l] [-a] <filename> [-t <type>] |
| picotool load [-v] [-r] <filename> [-t <type>] [--bus <bus>] [--address <addr>] |
| picotool save [-p] [--bus <bus>] [--address <addr>] <filename> [-t <type>] |
| picotool save -a [--bus <bus>] [--address <addr>] <filename> [-t <type>] |
| picotool save -r <from> <to> [--bus <bus>] [--address <addr>] <filename> [-t <type>] |
| picotool verify [--bus <bus>] [--address <addr>] <filename> [-t <type>] [-r <from> <to>] |
| picotool reboot [-a] [-u] [--bus <bus>] [--address <addr>] |
| 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 |
| 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. |
| reboot Reboot the device |
| 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 an RP2040 device in BOOTSEL mode to be connected. |
| |
| ## info |
| |
| So there is now _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 RP2040 devices in BOOTSEL mode, or from |
| a file. This file can be an ELF, a UF2 or a BIN file. |
| |
| ```asciidoc |
| $ 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 USB boot |
| mode |
| |
| SYNOPSYS: |
| picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] |
| picotool info [-b] [-p] [-d] [-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 |
| -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 |
| To target a file |
| <filename> |
| The file name |
| -t <type> |
| Specify file type (uf2 | elf | bin) explicitly, ignoring file extension |
| ``` |
| |
| e.g. |
| |
| ```text |
| $ picotool info |
| Program Information |
| name: hello_world |
| features: stdout to UART |
| ``` |
| |
| ```text |
| $ 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 |
| ``` |
| |
| ```text |
| $ picotool info -bp |
| Program Information |
| name: hello_world |
| features: stdout to UART |
| |
| Fixed Pin Information |
| 20: UART1 TX |
| 21: UART1 RX |
| ``` |
| |
| ```text |
| $ 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 |
| ``` |
| |
| ## save |
| |
| Save allows you to save a range of memory or a program or the whole of flash from the device to a BIN file or a UF2 file |
| |
| ```text |
| $ picotool help save |
| SAVE: |
| Save the program / memory stored in flash on the device to a file. |
| |
| SYNOPSYS: |
| picotool save [-p] [--bus <bus>] [--address <addr>] <filename> [-t <type>] |
| picotool save -a [--bus <bus>] [--address <addr>] <filename> [-t <type>] |
| picotool save -r <from> <to> [--bus <bus>] [--address <addr>] <filename> [-t <type>] |
| |
| 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 the range is expanded to 256 byte boundaries |
| <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 |
| File to save to |
| <filename> |
| The file name |
| -t <type> |
| Specify file type (uf2 | elf | bin) explicitly, ignoring file extension |
| ``` |
| |
| e.g. |
| |
| ```text |
| $ picotool info |
| Program Information |
| name: lcd_1602_i2c |
| web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c |
| ``` |
| ```text |
| $ picotool save spoon.uf2 |
| Saving file: [==============================] 100% |
| Wrote 51200 bytes to spoon.uf2 |
| ``` |
| ```text |
| $ 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 |
| ``` |
| |
| ## Binary Information |
| |
| Binary information is machine locatable and generally machine consumable. I say generally because anyone can |
| include any information, and we can tell it from ours, but it is up to them whether they make their data self describing. |
| |
| Note that we will certainly add more binary info over time, but I'd like to get a minimum core set included |
| in most binaries from launch!! |
| |
| ### Basic Information |
| |
| This information is really handy when you pick up a Pico and don't know what is on it! |
| |
| Basic information includes |
| |
| - program name |
| - program description |
| - program version string |
| - program build date |
| - program url |
| - program end address |
| - program features - this is a list built from individual strings in the binary, that can be displayed (e.g. we will have one for UART stdio and one for USB stdio) in the SDK |
| - build attributes - this is a similar list of strings, for things pertaining to the binary itself (e.g. Debug Build) |
| |
| Note it is my intention that things like MicroPython would include features for parts of the language/libraries they include. |
| |
| This might not be as a feature string per se, but could be another aggregating list (features/attributes are well known) |
| but we can add another piece of binary info to name a list attribute that aggregates strings with a particular tag, so you |
| could trivially add "MicroPython libraries:" etc to the `picotool` output without changing the tool itself. |
| |
| ### Pins |
| |
| This is certainly handy when you have an executable called 'hello_world.elf' but you forgot what board it is built for... |
| |
| Static (fixed) pin assignments can be recorded in the binary in very compact form: |
| |
| ```text |
| $ picotool info --pins sprite_demo.elf |
| File sprite_demo.elf: |
| |
| Fixed Pin Information |
| 0-4: Red 0-4 |
| 6-10: Green 0-4 |
| 11-15: Blue 0-4 |
| 16: HSync |
| 17: VSync |
| 18: Display Enable |
| 19: Pixel Clock |
| 20: UART1 TX |
| 21: UART1 RX |
| ``` |
| |
| ### Including Binary information |
| |
| Binary information is declared in the program by macros (vile warped macros); for the previous example: |
| |
| ```text |
| $ picotool info --pins sprite_demo.elf |
| File sprite_demo.elf: |
| |
| Fixed Pin Information |
| 0-4: Red 0-4 |
| 6-10: Green 0-4 |
| 11-15: Blue 0-4 |
| 16: HSync |
| 17: VSync |
| 18: Display Enable |
| 19: Pixel Clock |
| 20: UART1 TX |
| 21: UART1 RX |
| ``` |
| |
| ... there is one line in the `setup_default_uart` function: |
| |
| ```c |
| bi_decl_if_func_used(bi_2pins_with_func(PICO_DEFAULT_UART_RX_PIN, PICO_DEFAULT_UART_TX_PIN, GPIO_FUNC_UART)); |
| ``` |
| |
| |
| The two pin numbers, and the function UART are stored, then decoded to their actual function names (UART1 TX etc) by picotool. |
| The `bi_decl_if_func_used` makes sure the binary information is only included if the containing function is called. |
| |
| Equally, the video code contains a few lines like this: |
| |
| ```c |
| bi_decl_if_func_used(bi_pin_mask_with_name(0x1f << (PICO_SCANVIDEO_COLOR_PIN_BASE + PICO_SCANVIDEO_DPI_PIXEL_RSHIFT), "Red 0-4")); |
| ``` |
| |
| ### Details |
| |
| Things are designed to waste as little space as possible, but you can turn everything off with preprocessor var `PICO_NO_BINARY_INFO=1`. Additionally |
| any SDK code that inserts binary info can be separately excluded by its own preprocesor var. |
| |
| You need |
| ```c |
| #include "pico/binary_info.h" |
| ``` |
| |
| Basically you either use `bi_decl(bi_blah(...))` for unconditional inclusion of the binary info blah, or |
| `bi_decl_if_func_used(bi_blah(...))` for binary information that may be stripped if the enclosing function |
| is not included in the binary by the linker (think `--gc-sections`) |
| |
| There are a bunch of bi_ macros in the headers |
| |
| ```c |
| #define bi_binary_end(end) ... |
| #define bi_program_name(name) ... |
| #define bi_program_description(description) ... |
| #define bi_program_version_string(version_string) ... |
| #define bi_program_build_date_string(date_string) ... |
| #define bi_program_url(url) ... |
| #define bi_program_feature(feature) ... |
| #define bi_program_build_attribute(attr) ... |
| #define bi_1pin_with_func(p0, func) ... |
| #define bi_2pins_with_func(p0, p1, func) ... |
| #define bi_3pins_with_func(p0, p1, p2, func) ... |
| #define bi_4pins_with_func(p0, p1, p2, p3, func) ... |
| #define bi_5pins_with_func(p0, p1, p2, p3, p4, func) ... |
| #define bi_pin_range_with_func(plo, phi, func) ... |
| #define bi_pin_mask_with_name(pmask, label) ... |
| #define bi_pin_mask_with_names(pmask, label) ... |
| #define bi_1pin_with_name(p0, name) ... |
| #define bi_2pins_with_names(p0, name0, p1, name1) ... |
| #define bi_3pins_with_names(p0, name0, p1, name1, p2, name2) ... |
| #define bi_4pins_with_names(p0, name0, p1, name1, p2, name2, p3, name3) ... |
| ``` |
| |
| which make use of underlying macros, e.g. |
| ```c |
| #define bi_program_url(url) bi_string(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_URL, url) |
| ``` |
| |
| NOTE: It is easy to forget to enclose these in `bi_decl` etc., so an effort has been made (at the expense of a lot of kittens) |
| to make the build fail with a _somewhat_ helpful error message if you do so. |
| |
| For example, trying to compile |
| |
| ```c |
| bi_1pin_with_name(0, "Toaster activator"); |
| ``` |
| |
| gives |
| |
| ``` |
| /home/graham/dev/mu/pico_sdk/src/common/pico_binary_info/include/pico/binary_info/code.h:17:55: error: '_error_bi_is_missing_enclosing_decl_261' undeclared here (not in a function) |
| 17 | #define __bi_enclosure_check_lineno_var_name __CONCAT(_error_bi_is_missing_enclosing_decl_,__LINE__) |
| | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| ... more macro call stack of doom |
| ``` |
| |
| ## Setting common fields from CMake |
| |
| You can use |
| |
| ```cmake |
| pico_set_program_name(foo "not foo") # as "foo" would be the default |
| pico_set_program_description(foo "this is a foo") |
| pico_set_program_version_string(foo "0.00001a") |
| pico_set_program_url(foo "www.plinth.com/foo") |
| ``` |
| |
| Note all of these are passed as command line arguments to the compilation, so if you plan to use |
| quotes, newlines etc you may have better luck defining via bi_decl in the code. |
| |
| ## Additional binary information/picotool features |
| |
| ### SDK version |
| |
| Should add this; git revision in general is hard since the user may not have the SDK checked out from git, so we'll have to stick |
| a version number in a header |
| |
| ### 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. |
| |
| I suggest we tag these now... |
| |
| This is what I have right now off the top of my head (at the time) |
| ```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 |
| ```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 |
| BINARY_INFO_BLOCK_DEV_FLAG_WRITE = 1 << 1, |
| BINARY_INFO_BLOCK_DEV_FLAG_REFORMAT = 1 << 2, // may be reformatted.. |
| |
| BINARY_INFO_BLOCK_DEV_FLAG_PT_UNKNOWN = 0 << 4, // unknown free to look |
| BINARY_INFO_BLOCK_DEV_FLAG_PT_MBR = 1 << 4, // expect MBR |
| BINARY_INFO_BLOCK_DEV_FLAG_PT_GPT = 2 << 4, // expect GPT |
| BINARY_INFO_BLOCK_DEV_FLAG_PT_NONE = 3 << 4, // no partition table |
| }; |
| ``` |
| ### USB device descriptors |
| |
| Seems like tagging these might be nice (we just need to store the pointer to it assuming - as is often the case - |
| the descriptor is just a linear chunk of memory) ... I assume there is a tool out there to prettyify such a thing if picotool dumps the descriptor |
| in binary. |
| |
| ### Issues |
| |
| If you ctrl+c out of the middle of a long operation, then libusb seems to get a bit confused, which means we aren't able |
| to unlock our lockout of USB MSD writes (we have turned them off so the user doesn't step on their own toes). Simply running |
| `picotool info` again will unlock it properly the next time (or you can reboot the device). |