August 7, 2023

Raspberry Pi Pico - UF2 format

This was standardized by Microsoft and chosen for use by the Pico. There are at least 3 ways to start learning about this. One is to find some kind of standards document. This should be authoritative, but also sterile -- and they rarely give any history or perspective on why decisions were made. Another is to look at the code, in this case the boot loader code for the RP2040, and see what is actually implemented. Another is to read whatever discussions might exist about how and why this was done the way it was. In the case of the RP2040, other code can be studied, in particular the tool in the SDK that generates the uf2 file, as well as code written by others to generate uf2 files.

Here on a microsoft blog, we have a discussion of some of the "why". Microsoft created this file format to be used with their web based PXT (programming experience toolkit). One of the main goals was that it would allow beginning users to load code on devices that acted like mass storage devices. Another was to be 512 byte block oriented, to work nicely with existing flash memory devices.

The next link gives what seems to be the official description of the format.

The file is composed of 512 byte blocks. Each block has a 32 byte header, 476 bytes of payload, and a final 4 byte magic number. It seems to be pretty common to put only 256 bytes of actual payload into each block.

Some people calculate a CRC for the entire image and append it as part of the payload in the last block, but I see no mention of this in the documents and things seem to work just fine without it.

The SDK includes a uf2 utility in tools/elf2uf2 -- it is about 400 lines of C++, but it looks like plain C to me and looks fairly straightforward.

CRC required?

This is not part of the uf2 protocol. It is something required by the boot rom in certain cases.

The bootrom only wants a CRC to make it happy to load the first "sector" (256 bytes) from flash. It thinks this is the second stage boot loader and once it validates it, it jumps into it and it is responsible to load whatever else ought to be loaded.

So for simple demos, you have two choices. If they are less than 256 bytes, you can slap a checksum onto the end of that sector and the bootrom will load and run it thinking it is a second stage loader. If they are bigger than 256 bytes, you can send them via a UF2 file to ram and run them there. Of course they will vanish as soon as the device is reset or power cycled.

Code bigger than 252 bytes will require setting up a second stage boot loader if you want to put it on flash. The SDK knows all about this.


Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org