The puzzle
A byte in flash reads 0x3C. Your code writes 0x5A to the same address, the write reports success, and reading back gives 0x18, a value nobody wrote. Nothing is broken. Flash is not RAM, and the rule it follows is simple, strict and completely unlike the memory your variables live in. What is the rule, and how do you store a setting that must survive ten years of power cycles when each piece of flash can only be rewritten a few thousand times?
STEP 1
Two families: volatile and nonvolatile
Volatile memory needs power to keep its contents. Nonvolatile memory keeps them with the power off. A microcontroller needs both: nonvolatile storage for the program and anything that must survive a reset, and volatile working memory that is fast and can be written freely.
| Memory | Keeps data unpowered? | Read | Write | Typical use |
|---|---|---|---|---|
| SRAM | no | fast, any byte | fast, any byte, unlimited | variables, stack, heap |
| NOR flash | yes | fast enough to execute from | erase a sector, then program pages; limited cycles | program, constants, logs |
| EEPROM | yes | slow-ish, any byte | per byte, limited cycles (more than flash) | settings |
| ROM (mask ROM) | yes | fast | never: fixed at manufacture | boot code |
| OTP / fuses | yes | fast | once per bit | keys, calibration, configuration |
SRAM holds each bit in a small latch of transistors that stays in one state while powered. Any byte can be written in one bus cycle, as often as you like. Remove power and the contents become indeterminate: they cannot be relied on, though they are not necessarily random either. Lesson 6 returns to what that means after reset.
Flash holds each bit as charge trapped in an insulated part of a transistor, which stays put for years without power. Reading it is fast enough to execute code from. Writing it is the hard part.
STEP 2
Erase to ones, program to zeros
Flash writes in two separate operations with two different sizes:
- Erase sets every bit in a block to 1. The smallest erasable block is a sector: 4096 bytes on the flash chips the RP2040 SDK supports (
FLASH_SECTOR_SIZE). Erased flash reads 0xFF. - Program can change 1s to 0s, but never 0s to 1s. It is done in pages: 256 bytes for the same chips (
FLASH_PAGE_SIZE).
So the value left after programming is the old contents combined bitwise with the new value:
0x3C & 0x5A = 0x18, which is the puzzle from the opening. The SDK’s own documentation says it plainly: the only way to change a zero bit back to one is to erase the whole sector the page resides in.
The AND rule describes the external SPI NOR flash chips used with parts like the RP2040. Many microcontrollers’ internal flash adds error-correcting codes and forbids programming the same word or page twice between erases, even to clear more bits. Before relying on reprogramming a partly written page (the append scheme below does), check that your flash allows it.
↑ This step uses the figure at the top of the page.
This one rule shapes every piece of firmware that stores data. Updating one byte in place means erasing the whole 4096-byte sector, which erases the other 4095 bytes too, so they must be read into RAM first and written back. Erasing takes milliseconds, not nanoseconds. And if power fails between the erase and the rewrite, the old data is gone and the new data is not yet there (unit 14 lesson 5).
STEP 3
Endurance and retention
Each erase slightly damages the cells. The datasheet states two limits:
- Endurance: how many erase/program cycles each erasable block is specified to survive. The ATmega328P datasheet, for example, specifies 10 000 cycles for its flash and 100 000 for its EEPROM.
- Retention: how long data survives unpowered. The same datasheet gives 20 years at 85 °C and 100 years at 25 °C. Retention is shorter when hot and after many erase cycles; datasheets state the temperature, and often the number of cycles already endured, that a retention figure applies to.
Both are per-part numbers under stated conditions, exactly like the electrical limits in unit 1 lesson 6: use the numbers in your part’s datasheet, not these.
Endurance counts erases, and an erase covers a whole sector. That gives a lever. Instead of erasing to rewrite one record in place, append each new record after the last one in the sector, and erase only when the sector is full. With records per sector, rotating through sectors, each able to survive erases, and one record written every :
Rewriting in place is the special case .
Each flash sector survives a limited number of erase cycles (the endurance, from the part’s datasheet). Rewriting one record in place costs an erase per write. Appending records until a sector is full, and rotating through several sectors, spreads the erases out: that is wear levelling. The model assumes 4096-byte sectors and ignores metadata overhead.
Spreading erases across the storage is called wear levelling, and it is what flash file systems such as littlefs are built around. Their design notes put the problem bluntly: writing to flash is destructive, and a system that keeps writing to the same block will wear it out.
STEP 4
EEPROM, real and emulated
A true EEPROM erases and writes single bytes and typically has higher endurance than flash, which makes it pleasant for settings: AVR parts provide eeprom_update_byte(), which writes only if the value has changed, precisely to save endurance. Many modern microcontrollers have no EEPROM at all. Vendors then supply “EEPROM emulation” libraries that do what the formula above suggests: keep a log of records in two or more flash sectors, always append, and copy the live values to a fresh sector when one fills up.
ROM and OTP complete the picture. The RP2040’s 16 KiB boot ROM is read-only memory fixed when the chip is made; firmware cannot corrupt it. One-time-programmable fuses can be written once per bit and are used for keys, calibration and security settings (unit 15).
STEP 5
Worked example: storing a configuration for ten years
A device keeps a 40-byte configuration and saves it up to 10 times a day. The flash specifies erase cycles per 4096-byte sector. Will it last ten years?
Writes needed: saves.
In place: every save erases the sector, so the sector lasts saves, or days: about 2.7 years. It fails.
Appended records: give each record an 8-byte header with a sequence number and a checksum, so a record is 48 bytes and fit in a sector. One sector now absorbs saves, more than 23 times the requirement. Using two sectors in rotation, so that a valid copy always survives while the other sector is erased, doubles that again. Lifetime: years of daily writing, where 8640 s is one save every 2.4 hours.
The endurance limit stops being the constraint. What remains is power-loss safety, which the sequence numbers and checksums also provide: on boot, read every record, discard any whose checksum fails, and use the newest valid one.
MYTHS AND FACTS
Common misconceptions
Flash can be written like RAM
Programming can only clear bits. Setting any bit back to 1 needs a sector erase.
Erase sets memory to zero
Erased NOR flash reads as all ones (0xFF).
Endurance is per byte
It counts erase cycles of a whole sector. Appending records spreads one erase over many writes.
Reading flash wears it out
Endurance limits count erase/program cycles. Reads are not counted against them.
Retention is forever
It is a specified time, shorter at high temperature and for heavily cycled cells.
SRAM keeps its contents through a reset
It often does through a brief warm reset, but nothing guarantees it unless the chip documents it, and after power loss the contents are random (lesson 6).
Check yourself
Answer in your head, then open the card.
A flash byte holds 0xF0. What is stored after programming 0x3C without erasing? And after programming 0xC3?
0xF0 & 0x3C = 0x30. 0xF0 & 0xC3 = 0xC0. Neither is the value written, because both needed some 0 bits of 0xF0 to become 1.
Which new values can be programmed over 0xF0 without an erase?
Exactly those whose 1 bits are a subset of 0xF0’s: any value of the form 0xN0 where N’s set bits are within 0xF, i.e. 0x00, 0x10, 0x20, … 0xF0. Formally, new & ~old must be 0.
A logger writes one 32-byte record per second into 4096-byte sectors with 100 000-cycle endurance, rotating through 8 sectors. How long until the flash wears out?
. s, about 3.2 years.
Why does erasing a sector to update one setting risk losing all the settings?
Erase clears all 4096 bytes. Between the erase and the reprogramming, the only copy of the other settings is in RAM; a reset or power failure in that window loses them. Appending to a second sector avoids ever erasing the only valid copy.
Sources (5)
- Raspberry Pi Ltd, pico-sdk 2.0.0, src/rp2_common/hardware_flash/include/hardware/flash.h — FLASH_PAGE_SIZE (1u << 8) and FLASH_SECTOR_SIZE (1u << 12); erase offsets must be 4096-byte aligned and programs 256-byte aligned; “Programming a flash page effectively changes some of the bits from one to zero. The only way to change a zero bit back to one is to ‘erase’ the whole sector”; the functions are unsafe while the other core or an interrupt handler executes from flash
- littlefs, DESIGN.md “The design of littlefs”, sections “The problem” and “Wear leveling” — why flash needs power-loss resilience and wear leveling: “Writing to flash is destructive. If a filesystem repeatedly writes to the same block, eventually that block will wear out”
- Microchip, ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet, DS40002061B (Rev. B, 08/2020), “Features”, p. 1 — an example of endurance and retention specifications: write/erase cycles 10 000 for flash and 100 000 for EEPROM; data retention 20 years at 85 °C and 100 years at 25 °C
- Raspberry Pi Ltd, pico-bootrom-rp2040, bootrom/bootrom.ld — the boot ROM’s linker script: 16K of ROM at address 0
- avr-libc, include/avr/eeprom.h — byte-level EEPROM access (eeprom_read_byte, eeprom_update_byte) on parts with a true EEPROM; the update functions “read each byte first and skip the burning if the old value is the same”