STM32F767: Two Megabytes, Two Banks, and a Boundary at Half Way
Two megabytes of flash on a microcontroller sounds like a simple quantity, and the tools treat it as one: a length, an offset, a single block of output. The silicon does not work that way. The array is two banks of one megabyte each, with a boundary in the middle that is not visible in a hex dump and that governs erase granularity, write protection and the possibility of updating half the device while the other half runs.
None of this stops a read from succeeding. It stops the conclusions drawn from the read from being right.
Where the boundary is
The first bank occupies the lower megabyte of the flash address range and the second bank occupies the upper megabyte. The boundary falls at the address one megabyte above the flash base, which in a flat image is offset one million and forty-eight thousand five hundred and seventy-six.
| range | contents | what it governs |
|---|---|---|
| lower bank, first megabyte | normally the program, its vector table and its constants | the reset vector and the entry point |
| upper bank, second megabyte | a second image, application data, or nothing | the update path and the fallback |
The row that matters is the third column. A device that uses the upper bank for a stored update has a program in the lower bank and a payload in the upper one, and a flat dump of two megabytes contains both with nothing marking the join. A reader who assumes the second megabyte is unused space will treat a valid second image as garbage, and a reader who assumes it is a continuation of the first will disassemble the join as a single program and find nonsense at the seam.
| observation in a flat dump | the wrong conclusion | the better reading |
|---|---|---|
| code ends before the boundary, upper bank erased | the device has one small program | consistent with a device whose second bank was never populated |
| upper bank contains a plausible vector table | the array holds two programs | an update is staged, or the device runs from the upper bank |
| a valid program in each bank with different sizes | the image is corrupted | two firmware versions are both present |
| upper bank starts with a header and a length field | the data is unreadable | a packaged update, not code |
Erase granularity depends on the mode
This is the detail that catches people who work from memory of another family. The sector size on this part is not a constant; it follows the bank configuration.
| configuration | sector size | sectors | effect on the image |
|---|---|---|---|
| single-bank mode | 256 KB | eight across the whole array | erase units are large, so small updates rewrite a lot |
| dual-bank mode | 128 KB | eight per bank | finer granularity, and the two banks can be treated separately |
The consequence for a recovery is about interpreting what is blank. In single-bank mode, an erased region is at least 256 KB, and a program that occupies less than that leaves a large uniform expanse of erased values. In dual-bank mode, the same program leaves 128 KB boundaries. Looking at the size of the erased runs is therefore a way to infer the mode the device was configured in, which in turn tells you whether the design intended to update one bank while running from the other.
That inference is worth making because it is cheap and it is independent of anything the customer says. A device whose firmware uses a bank-swap style update will show a clean 128 KB alignment; a device that never updates in place will show whatever alignment the linker chose.
Read while write
The reason the part has two banks is that one can be erased and programmed while code executes from the other. This is the hardware basis of every robust field-update design, and it is also the mechanism that produces the most confusing partial states.
An update proceeds in stages: the new image is received and programmed into the idle bank, verified, and then a marker is written that makes the next reset start from the new bank. Interruption at each stage leaves a different artefact.
| interruption point | what the device holds | what the next boot does |
|---|---|---|
| during programming of the idle bank | a partial image in the idle bank, the old image intact | runs the old image, because the marker was never written |
| after programming, before the marker | a complete new image that is not in use | runs the old image; the new one is orphaned |
| after the marker, before the old bank is erased | two complete images, one designated | runs the new image |
| during erase of the old bank | one complete image and one partially erased | runs the new image, with no fallback |
The second row is the one that generates support tickets. The device behaves exactly as it did before the update, the customer reports a failed update, and the actual state is that the update succeeded and was never activated. A recovery from such a device returns two programs, and the question is not which one is valid but which one the marker points at.
Write protection is set per bank
Protection of the array against writing is configured in option bytes, with a granularity that follows the regions rather than the whole device. That matters because a design can protect the lower bank — the one that must not be damaged in the field — and leave the upper bank writable for updates.
When such a device is recovered, the protection state is part of the answer. A customer who intends to restore the device needs to know which regions will refuse programming, and a customer who intends to modify the firmware needs to know that the modification will be rejected by hardware rather than by software. Reading the option bytes and decoding them into a list of write-protected ranges takes a minute and prevents an afternoon of confusion.
The read-out protection is a separate mechanism with its own two levels, and its interaction with the boot path is described in STM32F745 heximal extraction. The contrast with a family that goes further and lets the two banks exchange their base addresses at runtime is in STM32H743 flash recovery — there the mapping itself is configurable, which produces two correct but different answers to the same question.
Reading the array correctly
Three steps, in order. First, read the option bytes and decode the bank mode and the write protection. Second, read the array as a whole; the read itself is indifferent to banks. Third, analyse the result bank by bank, because the analysis is not indifferent.
The third step is where the join has to be handled explicitly. A vector table at the flash base tells you where the program starts. A second vector table at the boundary tells you that a second program exists. Neither tells you which one runs, and the answer to that question lives in the option bytes or in a marker written by the update logic, which is why the first step comes first.
What the report should say
The report should name the bank mode, the sector size that follows from it, the write protection ranges found, and the contents of each bank as a separate statement. Where two images are present it should say which is designated active and on what evidence, and where a staged update was interrupted it should say so plainly rather than presenting a partial image as a corruption.
The RAM map that sits below all of this, and misleads anyone who assumes it follows the usual pattern, is in STM32F765 heximal recovery. The external memory path that often holds the rest of a large application is in STM32F769 program recovery, and the case where the internal array is far too small to hold the product at all is in STM32F750 flash recovery.
FAQ
Does the part support swapping the two banks’ base addresses? Not in the sense that later families do. The banks have fixed addresses; what an update changes is which bank the boot process uses, recorded in configuration rather than by remapping the array.
Is a two-megabyte dump always two megabytes of program? No. It is two megabytes of array, which may contain one program, two programs, one program and a staged update, or a program and a great deal of erased space.
Can the array be read while the device runs? Yes, and the bank that is not executing can be erased at the same time, which is the whole point of the arrangement.
Conclusion
The quantity is two megabytes; the structure is two banks with a boundary, a mode-dependent sector size and separate protection. Reading the bytes is the easy part, and it is also the part that creates the false confidence. Naming the banks, the mode and the designated image is what turns a dump into a description of a device.