The blocks of a UF2 file are not the program: the program is what a board holds in flash after the blocks have been written. This page describes how the explorer puts the blocks together, how it decides which processor the code is for, and where it looks for the first instructions. The format itself is described on blocks and flags.
Assembling the image
- Only blocks with correct magic numbers, without the not-main-flash flag and without the file-container flag are written to flash.
- The blocks are grouped by family ID (blocks without one are a group of their own), and each group is its own image.
- Each block’s payload goes to its target address. Blocks that touch make one segment; blocks with a hole between them make two. A hole is reported as a gap, and its size is the number of bytes of flash that the file does not write.
- Where two blocks write the same addresses, the one later in the file wins, as when a board writes them in arrival order. The explorer reports it as an overlap.
- A block written at a lower address than the block before it, in the same family, is counted as out of order. That is normal for some tools and is not a fault.
- When the file holds several families the one with the most data is shown; the choice Family to show picks another.
A real example is Pimoroni’s MicroPython for the Presto, presto-v1.0.0-micropython-with-filesystem.uf2: one family
(RP2350_ARM_S), two segments (the firmware from 0x10000000 and a filesystem from 0x10200000) and a gap of
831,488 bytes between them. Another, flash_nuke.uf2, holds two families whose blocks go to the same RAM addresses, one for
the RP2040 and one for the “absolute” download used by the RP2350.
Which processor
A UF2 file never says which processor runs its code; only the family ID hints at the chip. The explorer keeps a table of the processor core of each chip family it knows, which is its own table, made from the manufacturers’ chip names as we know them, not data from the file or from the UF2 repository (which lists the families only). It is used for the default of the Processor choice; the choice overrides it. A family that is not in the table is shown as bytes only until a processor is chosen.
| Family ID | Name | Core in our table |
|---|---|---|
0x1851780a | SAML21 | Cortex-M0+ |
0x1b57745f | NRF52 | Cortex-M4 |
0x621e937a | NRF52833 | Cortex-M4 |
0xada52840 | NRF52840 | Cortex-M4 |
0x820d9a5f | NRF52820 | Cortex-M4 |
0x72721d4e | NRF52832xxAA | Cortex-M4 |
0x6f752678 | NRF52832xxAB | Cortex-M4 |
0x1c5f21b0 | ESP32 | Xtensa |
0x7eab61ed | ESP8266 | Xtensa |
0xbfdd4eee | ESP32S2 | Xtensa |
0xc47e5767 | ESP32S3 | Xtensa |
0xd42ba06c | ESP32C3 | RV32IMC |
0x2b88d29c | ESP32C2 | RV32IMC |
0x540ddf62 | ESP32C6 | RV32IMAC |
0x332726f6 | ESP32H2 | RV32IMAC |
0x1e1f432d | STM32L1 | Cortex-M3 |
0x5d1a0a2e | STM32F2 | Cortex-M3 |
0x5ee21072 | STM32F1 | Cortex-M3 |
0x202e3a91 | STM32L0 | Cortex-M0+ |
0x300f5633 | STM32G0 | Cortex-M0+ |
0x647824b6 | STM32F0 | Cortex-M0 |
0x57755a57 | STM32F4 | Cortex-M4 |
0x6b846188 | STM32F3 | Cortex-M4 |
0x6d0922fa | STM32F407 | Cortex-M4 |
0x8fb060fe | STM32F407VG | Cortex-M4 |
0x4c71240a | STM32G4 | Cortex-M4 |
0x00ff6919 | STM32L4 | Cortex-M4 |
0x70d16653 | STM32WB | Cortex-M4 |
0x21460ff0 | STM32WL | Cortex-M4 |
0x2dc309c5 | STM32F411xE | Cortex-M4 |
0x06d1097b | STM32F411xC | Cortex-M4 |
0x53b80f00 | STM32F7 | Cortex-M7 |
0x6db66082 | STM32H7 | Cortex-M7 |
0x4fb2d5bd | MIMXRT10XX | Cortex-M7 |
0x04240bdf | STM32L5 | Cortex-M33 |
0x4e8f1c5d | STM32H5 | Cortex-M33 |
0x2abc77ec | LPC55 | Cortex-M33 |
0x55114460 | SAMD51 | Cortex-M4 |
0x68ed2b88 | SAMD21 | Cortex-M0+ |
0x7f83e793 | KL32L2 | Cortex-M0+ |
0x7be8976d | RA4M1 | Cortex-M4 |
0xe48bff56 | RP2040 | Cortex-M0+ |
0xe48bff59 | RP2350_ARM_S | Cortex-M33 |
0xe48bff5b | RP2350_ARM_NS | Cortex-M33 |
0xe48bff5a | RP2350_RISCV | Hazard3 RV32 |
0x9af03e33 | GD32VF103 | RV32IMAC |
0xde1270b7 | BL602 | RV32IMAFC |
The ARM cores map to architecture levels: Cortex-M0 and M0+ to ARMv6-M, M3 to ARMv7-M, M4 and M7 to ARMv7E-M, and the RP2350’s M33 to
ARMv8-M Mainline with DSP. The Hazard3 cores of the RP2350 are read as RV32IMAC with the Zcb, Zcmp, Zcmt and Zb* extensions. Families
not in the table have no default; the family of the RP2XXX absolute and data downloads (0xe48bff57, 0xe48bff58) hold
no code of their own.
The Cortex-M vector table
A Cortex-M processor starts by reading two words from its vector table: the initial stack pointer, then the address of the reset handler. The words after them are the addresses of the other exception handlers; each has its low bit set, the Thumb bit, because a Cortex-M runs Thumb instructions only. After the 16 system words come one word per device interrupt.
| Word | Exception | ARMv6-M | ARMv7-M and ARMv7E-M | ARMv8-M Mainline |
|---|---|---|---|---|
| 0 | Initial stack pointer | yes | yes | yes |
| 1 | Reset | yes | yes | yes |
| 2 | NMI | yes | yes | yes |
| 3 | HardFault | yes | yes | yes |
| 4 | MemManage | reserved | yes | yes |
| 5 | BusFault | reserved | yes | yes |
| 6 | UsageFault | reserved | yes | yes |
| 7 | SecureFault | reserved | reserved | yes |
| 8–10 | reserved | reserved | reserved | reserved |
| 11 | SVCall | yes | yes | yes |
| 12 | DebugMonitor | reserved | yes | yes |
| 13 | reserved | reserved | reserved | reserved |
| 14 | PendSV | yes | yes | yes |
| 15 | SysTick | yes | yes | yes |
| 16 and on | IRQ 0, 1, … | device | device | device |
The names are from the ARM architecture reference manuals as we know them; the viewer did not read the manuals. The table above is also how the viewer names the words; SecureFault is shown for ARMv8-M Mainline although a core without the Security Extension does not have it.
How many interrupts
The file does not say how many interrupts the chip has, so the explorer shows a number of words you choose: 48 by default (the 16 system words and 32 interrupts). The interrupt controller of a Cortex-M0 and M0+ has room for up to 32 interrupts, that of the larger cores for up to 240; a chip has fewer than its core allows. The words after the real table are code or data, so a larger number shows some of those as handlers. To keep code visible, the table is cut short where the first handler of the 16 system words lies at or after the 64th byte of the table: the words from there on are code, not table. A short table, with the reset handler right behind it, is therefore shown with only the words before the handler (at least 16).
Where the table is
The table is at the start of the image in most Cortex-M firmware. A flash image for the RP2040 is different: its boot ROM copies
256 bytes from the start of flash, checks them with a CRC-32 stored in the last four bytes, and runs them as a second-stage
bootloader that sets up the flash for execution in place; the program, with its vector table, follows at 0x10000100.
An image that is loaded into RAM, such as flash_nuke.uf2, may start with code instead.
The Vector table choice therefore offers: at the start, 256 bytes after the start, or none. Its default is the first of those two
positions (the second first for an RP2040 image in flash) that holds a table the viewer accepts: a stack pointer that is a multiple of 4
between 0x10000000 and 0x3FFFFFFF, a reset handler with the Thumb bit set, and every other handler zero or with the
Thumb bit set. If neither does, the default is none. That is a check of the words, not a way of telling code from data, and the choice
overrides it. The disassembly then opens at the reset handler, or at the start address you give.
Register and memory names
The viewer names the addresses in the ARMv6-M system region and, for the RP2040, the memory regions and peripheral bases. The names
are those of the Raspberry Pi Pico SDK headers (pico-sdk:
hardware/regs/m0plus.h and addressmap.h), as read in November 2026. They label an address that a branch or
a reference lands on exactly; the viewer does not resolve the addresses that a program builds in registers.
| Address | Name | Meaning |
|---|---|---|
0xE000E010 | SYST_CSR | SysTick control and status |
0xE000E014 | SYST_RVR | SysTick reload value |
0xE000E018 | SYST_CVR | SysTick current value |
0xE000E01C | SYST_CALIB | SysTick calibration value |
0xE000E100 | NVIC_ISER | Interrupt set-enable |
0xE000E180 | NVIC_ICER | Interrupt clear-enable |
0xE000E200 | NVIC_ISPR | Interrupt set-pending |
0xE000E280 | NVIC_ICPR | Interrupt clear-pending |
0xE000E400 | NVIC_IPR0 | Interrupt priority 0 (interrupts 0-3); IPR1 to IPR7 follow every 4 bytes |
0xE000ED00 | CPUID | CPUID base register |
0xE000ED04 | ICSR | Interrupt control and state |
0xE000ED08 | VTOR | Vector table offset |
0xE000ED0C | AIRCR | Application interrupt and reset control |
0xE000ED10 | SCR | System control |
0xE000ED14 | CCR | Configuration and control |
0xE000ED1C | SHPR2 | System handler priority 2 (SVCall) |
0xE000ED20 | SHPR3 | System handler priority 3 (PendSV, SysTick) |
0xE000ED24 | SHCSR | System handler control and state |
0xE000ED90 | MPU_TYPE | MPU type |
0xE000ED94 | MPU_CTRL | MPU control |
0xE000ED98 | MPU_RNR | MPU region number |
0xE000ED9C | MPU_RBAR | MPU region base address |
0xE000EDA0 | MPU_RASR | MPU region attributes and size |
The RP2040 memory regions and peripheral bases are listed in the viewer: XIP_BASE 0x10000000, SRAM_BASE
0x20000000, CLOCKS_BASE 0x40008000, IO_BANK0_BASE 0x40014000, UART0_BASE
0x40034000, TIMER_BASE 0x40054000, SIO_BASE 0xD0000000, PPB_BASE
0xE0000000 and the others. The RP2350 and the other chips have no peripheral names here; only the system region is named for them.
Sources
- microsoft/uf2 – the format and the list of families
- pico-sdk – the register and address names of the RP2040
- SEGGER Knowledge Base: RP2040 – the 256-byte second stage and its CRC
- The RP2040 bootloader – the boot sequence and the vector table after the second stage