Skip to main content

A flexible and cleanish framework for dealing with UF2 files

Project description

uf2utils

Python UF2 file utils in a decent framework

Copyright © 2024 Pat Deegan psychogenic.com

What is this insanity? Yes, the UF2 file format is not complex and it's a small matter to parse the thing, but why could I not find a library to do this intelligently?

Anyway, here's something for this odd, filled with magic but no checksum, format.

This library allows you to:

  • read in existing UF2 files and inspect the headers and payload, as well as extract all or potions of the contents;

  • augment existing UF2 files, used here to include a default filesystem for the RP2040/Pi Pico type boards; and

  • prepare your own UF2 files with whatever you want within, from scratch

and includes a sample script to dump info from a UF2 file, producing output like

$ python uf2utils/examples/uf2_info.py /tmp/RPI_PICO-custom.uf2
UF2 info
File /tmp/RPI_PICO-custom.uf2
        spanning 0x10000000 - 0x101fff00 in 8192 blocks
        Total payload size: 2097152 bytes
        Contains NO gaps.
        All blocks have board family value
        Board family set:
                Raspberry Pi RP2040
        Flags found
                0x2000: 8192 blocks

$ python uf2utils/examples/uf2_info.py /tmp/arcade.uf2
UF2 info
File /tmp/arcade.uf2
        spanning 0x0 - 0x807dd00 in 5270 blocks
        Total payload size: 1349120 bytes
        CONTAINS GAPS: 522726 must be generated to fill
        All blocks have board family value
        Board family set:
                Unknown board family 0x7193c
                Microchip (Atmel) SAMD51
                ST STM32F4xx
        Flags found
                0x1000: 1818 blocks
                0x2000: 3452 blocks



Examples

You want to add files to the standard raspberry pi MicroPython Pi Pico UF2 file and create a customized mini distro?

Or how about leaving the uPython OS as is and just replacing the contents of entire filesystem with a drag and drop onto the bootloader?

Easy peasy. To base an entire distro, and recursively include all the contents of /tmp/mypython at the root of the filesystem, you can run:

python uf2utils/examples/custom_pico.py \
       --fs_root /tmp/mypython \ 
       --upython /tmp/RPI_PICO-20240222-v1.22.2.uf2 \
       --out /tmp/rpi-custom.uf2

If you want a smaller file that you can use to update only the contents of the LittleFS filesystem with, you can instead do

python uf2utils/examples/pico_fs_update.py \
     --fs_root /tmp/ttupython \
     --out /tmp/meFS.uf2

Either way, hold the boot button, plug in the RP2040 device and drag the resulting --out file to the virtual drive. After update, the system should reboot and have your new contents.

The uf2utils.examples also contains a sample of extracting payloads from UF2 files.

Extra info about Pico/RP2040 MicroPython UF2

In addition to figuring out how to deal with the massively redundant UF2 files, some stumbling around was needed to actually get a conjoined OS + filesystem UF2 for some custom RP2040-based boards.

I include some notes and discoveries here, made through research, debugging with Uri and a lot of trial and error, in the hopes that you won't need to fumble around as much. This all applies to the Pico/RP2040 bootloader, uncertain how general these truths may be but none of them should hurt any platform.

The block number and block total matter. The UF2File objects have a renumber_blocks() that handle this simple matter, and it will be called automatically if you "dirty" the file by using append_payload().

The RP2040 MicroPython filesystem is LittleFS and its a nice simple fs, with a pretty nifty viewer online. When you create it, the blocksize must be 4096 and I think the OS is unhappy if you have a value other that 352 blocks in there. The default MicroPython _boot.py will just wipe anything it's unhappy with.

With RP2040 uPython, the FS is on the flash at 0xA0000 -- and this is a value, __vfs_start or something, that is really painful to try and discover, or at least it was for me. When creating the UF2 file, this offset must become relative to FLASH(rx) : ORIGIN = 0x10000000, so 0x100a0000.

The UF2 "blocksize"--each block in UF2 is fixed at 512 bytes, here I mean the size of each block's payload--needs to be 256, such that each block received by the bootloader writes a page to the flash. So, your payload winds up in a 2x bloated file, where almost half the payload bytes are forced to be zeros and then there are the no less than 12 bytes of "magic". So, a fat file.

The UF2 format actually is rather nice in that each block is independent and we could, in theory, skip over zones we don't care about. But then the RP2040 bootloader really doesn't like that. The last datablock in the current RPI_PICO-20240222-v1.22.2.uf2 is at offset 0x1004f500, and I'm adding the FS down at 0x100a0000. Turns out, you really must pad everything to fill the gap with empty datablocks, otherwise the bootloader gets sad and you don't get your files.

For this specific case, the UF2File class constructor has a fill_gaps boolean, or you can just manually use generate_blocks_for_gaps() (assuming the blocks are sorted). Files gets bigger but now we can update OS and FS in one go, great for factory.

Another option, for size or just later updates of FS, is to generate only the filesystem. That works fine. The start offset is 0x100a0000 and there are no gaps, and everyone is happy. See the pico_fs_update module in examples for that.

License

This library is release under the LGPL. See the LICENSE files for details.

2024-03-14, happy Pi day, have fun/cheers, Pat Deegan

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

uf2utils-0.9.2.tar.gz (25.1 kB view details)

Uploaded Source

Built Distribution

uf2utils-0.9.2-py3-none-any.whl (27.2 kB view details)

Uploaded Python 3

File details

Details for the file uf2utils-0.9.2.tar.gz.

File metadata

  • Download URL: uf2utils-0.9.2.tar.gz
  • Upload date:
  • Size: 25.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.10.12

File hashes

Hashes for uf2utils-0.9.2.tar.gz
Algorithm Hash digest
SHA256 cd86ca442dd6ee82bd0ad3f97b6d66558f70a1f89108e0e2eecfaa0e50ee8744
MD5 619e171ad97cec6c7d2c824389e4dff3
BLAKE2b-256 6b94be1059e4620c12c9c9efa17cf1018f0eb088dfd71a961040585488c38995

See more details on using hashes here.

File details

Details for the file uf2utils-0.9.2-py3-none-any.whl.

File metadata

  • Download URL: uf2utils-0.9.2-py3-none-any.whl
  • Upload date:
  • Size: 27.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.10.12

File hashes

Hashes for uf2utils-0.9.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c5359004c3b8d82a759912e7882a95e9ea6a1438149e1cb17aae722fcf43ace3
MD5 46fbbb5631e761930ce2c879e35b7797
BLAKE2b-256 75e461cb4f78436b9e3700c6a5375a5c840015de4eb64fe888ba0bfda302d23c

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page