STM32F745: The Program in ROM That Decides How the Job Starts
Before any bench equipment is connected, a device of this family is already running a program that its owner did not write and cannot remove. It sits in a read-only region of the address space, it is entered by holding configuration pins at reset, and it implements a command set over a serial interface. It exists so that a blank device can be programmed at the factory, and it remains in every shipped product.
That program is the first thing worth talking to. Its responses classify the device in a way that no amount of documentation can, and its limits are precise enough to plan around.
What it is and how it is entered
The bootloader is selected at reset by sampling two configuration pins. One combination boots the application from flash, another enters the loader, and a third is reserved. Designs wire these pins to fixed levels or to a connector, and the choice is visible on the schematic in about thirty seconds.
| pin state | what the device does at reset |
|---|---|
| both low | boots the application from flash |
| one high | enters the factory bootloader, which then waits on a serial interface |
| the other high | reserved, behaviour not defined for application use |
| both high | enters the factory bootloader |
The loader negotiates a serial protocol: a synchronisation byte, an acknowledgement, then commands. It runs from an internal oscillator, so it works on a board with a dead crystal, a shorted peripheral or a program that never reaches its first instruction. That is the reason it is worth trying first on a device described as dead.
The command set
The set is small, and each command answers a different question.
| command | what it returns | what it establishes |
|---|---|---|
| initialise | the loader version and the list of supported commands | that the interface works and what else is available |
| identify | the device identification word and the flash size in kilobytes | which die is fitted and how large the array is |
| read | the contents of a memory range | the image, when permitted |
| write | acknowledgement, or an error | whether the target region is writable |
| erase | acknowledgement after a mass erase | that the array is now blank, irreversibly |
| read protection status | whether protection is set | the classification that decides the whole job |
| jump | none, the device restarts into the application | a clean exit without a power cycle |
The second row is useful even when nothing else works. A dead board that still answers identify has told you the die, the array size and, by implication, which memory map applies — the same information a debugger would give, obtained without a working clock.
The sixth row is the one to issue second, immediately after identify. It is a single transaction and it splits every incoming job into two categories.
How protection is enforced
Protection is not a single switch. It is applied per command, and the pattern is consistent.
| command | protection unset | protection level one | protection level two |
|---|---|---|---|
| identify | works | works | works, but the debug path is gone |
| read | works | refused | refused |
| write to unprotected region | works | works | works |
| mass erase | works | works, and clears the array | works, and clears the array |
| read protection status | reports unset | reports set | reports set |
| jump to application | works | works | works |
The asymmetry in the second and fourth rows is the whole story of this class of job. The loader will happily destroy the array on a protected device and will refuse to show it, which means the one command a protected device still accepts is the one that makes recovery impossible.
| what the loader says | what it means | what follows |
|---|---|---|
| read succeeds | protection is unset | read the array and finish |
| read refused, erase accepted | level one | the flash contents are not obtainable through this path |
| read refused, identify works | level one or two | distinguish by attempting a debug attach |
| no response at all | the loader is not being entered | check the pins, the interface and the supply before concluding the device is dead |
The one-word test
The most efficient habit in this work is to attempt a read of a single word immediately after identify, before anything else is discussed. It costs one transaction and it converts an open question into a decided one.
A successful single-word read establishes that the entire array is readable through the same path, because the protection check is per request rather than per region. A refusal establishes that no read command will work at any address, which removes an entire category of plan from consideration and points the work at the debug port and, if that is also closed, at physical access.
What it does not establish is whether the refusal was caused by protection or by a malformed request. The two produce the same negative acknowledgement on the wire, which is why the protection status command exists and why issuing it second is worth the extra transaction.
What the ROM path cannot do
Four things, each of which is proposed regularly as a workaround.
It cannot read a protected array, at any address, by any arrangement of commands. It cannot bypass the protection by starting a write and reading back the result, because a write to a protected region is refused before it reaches the array. It cannot be replaced with a modified loader, because the region is read-only by construction. And it cannot be made to run user code with the protection temporarily lifted, because there is no such state.
The one thing it can do that is sometimes overlooked is execute a jump to an address outside flash. A device whose application has been damaged can be given control of a small routine placed in RAM, and that routine can do what the loader will not — provided the protection permits reading the array at all. This is a narrow path and it depends entirely on the first classification; where protection is unset, the loader’s own read command is simpler and faster.
Where this fits in a job
The order is fixed. Enter the loader if the pins allow it, identify the device, read the protection status, attempt a one-word read, and only then plan the work. A job that starts at the debug port skips the cheapest diagnostic available, and a job that starts with physical work may be skipping two cheaper routes.
The member of the family with two USB controllers, where this loader is reachable over USB and where the descriptor tables provide the product’s own documentation, is in STM32F723 binary recovery. The protection levels described from the crypto side, including the case where a key is derived from the device identifier, are in STM32F756 code recovery, and the stripped member where the loader is often the only interface the product exposes is in STM32F722 code recovery.
FAQ
Does entering the loader erase anything? No. It runs from read-only memory and waits for commands; only the erase command changes the array.
Can the loader read a device protected at level one? No, and there is no sequence of commands that changes that. Level one also blocks the debug port.
Why does the loader still accept an erase on a protected device? Because erasing is how a protected device is returned to service, and the array is destroyed in the process. That is the intended behaviour and the opposite of a recovery.
Conclusion
The factory loader is the cheapest diagnostic instrument on the board: it runs without the application, it reports the die and the array size, and it classifies protection in two short transactions. Its refusal to read is not an obstacle to be worked around but a fact that redirects the job, and knowing that before quoting is worth more than any single piece of equipment.