STM32F746: Why Two Reads of the Same Address Disagree
Every recovery job rests on a simple assumption: an address has a value. On this part that assumption needs a qualifier, because the processor does not read memory. It reads a 4 KB data cache, which reads memory when it has to. Between the two there is a window in which the cache holds a newer value than the memory does, and during that window the answer to “what is at this address” depends entirely on who is asking.
That is not a fault and not a rare condition. It is the normal state of a running device with a write-back cache, and it is the reason a live acquisition can produce an image that is neither the flash contents nor the RAM contents but a mix of both, frozen at slightly different moments.
A core that does not always read memory
The core in this device is a Cortex-M7 with a 4 KB instruction cache and a 4 KB data cache. The two caches are not symmetric in their consequences. The instruction cache matters for performance measurement and almost never for extraction, because code is not being modified while it runs. The data cache matters for extraction, because application state lives in RAM and application state is exactly what a recovery is trying to capture.
The data cache is write-back. When the core stores a word, the store lands in the cache line and the line is marked dirty. The write to the memory behind it is deferred until the line is evicted, either because the cache needs the space or because software explicitly cleans it. On a device running an interrupt-driven loop, evictions happen constantly and the window is short. On a device sitting in a tight polling loop with a small working set, the same lines can stay dirty for a very long time.
| who reads the address | what it sees | why |
|---|---|---|
| the core, through a normal load | the newest value | it reads the cache first |
| the core, after a cache invalidate | whatever memory holds | the invalidate discarded the dirty line |
| a debug probe over the AHB access port | whatever memory holds | the probe bypasses the cache entirely |
| a DMA engine | whatever memory holds | DMA is not cache-coherent on this part |
| a second bus master reading through the same path | the memory value | there is no snooping hardware |
The third row is the one that decides how a live read has to be done. A debug probe connected over the access port sees memory, not cache. That is usually described as an advantage, because it gives a stable and repeatable view. It is only an advantage if the value being captured is one that has already been written back.
Three ways this shows up in a recovery
The first is a torn parameter block. A device that stores calibration values in RAM and writes them back to flash on a timer will have the newest values in the cache. A probe reading the RAM copy gets the previous set. If the recovery reports the RAM copy, the customer compares it against the values the device displays on its own screen and finds a mismatch that looks like a reading error.
The second is a program that appears to branch into uninitialised memory. The stack holds return addresses and local variables. When the stack region is dirty, the memory image of it is older than what the core is actually using. A disassembly built from stack contents then produces a call chain that does not correspond to anything.
The third is the most expensive: a state machine captured one step behind. A device that keeps its current mode in a static variable, written on every transition, will show the previous mode in a probe read. The customer’s complaint is that the extracted logic is wrong, and the extracted logic is a faithful copy of a value that was true a few hundred microseconds earlier.
| observation | plausible wrong conclusion | what is actually happening |
|---|---|---|
| RAM copy differs from the value shown on the device’s display | the read is unreliable | the display reads the cache, the probe reads memory |
| a variable changes on every read of the same address | the device is still running | the cache is being evicted between reads |
| the stack looks like it belongs to an older call | the image is corrupted | the newest stack frames were never written back |
| a watchdog counter reads as a stale value | the watchdog is not running | its update sits dirty in a cache line |
Reading around it
Three approaches work, and they differ in how much they disturb the device.
| approach | how it works | trade-off |
|---|---|---|
| halt the core, then clean and invalidate the cache | after the halt, force a write-back of all dirty lines, then read | the halt changes timing; a device with a watchdog may reset unless the watchdog is frozen |
| read the cache contents as well as memory | extract both and reconcile | requires knowing the cache geometry and the mapping, which is where errors enter |
| capture under a repeated read, then take the modal value | read the same range many times and keep the most frequent value per address | slow, and wrong for genuinely changing data |
The first is what a careful bench does, and it has a side effect worth stating in the report: freezing the watchdog is a change to the device’s configuration. On a part where the watchdog runs from an independent oscillator and cannot be frozen while the core is halted for more than a short window, the halt has to be short enough to fit inside one timeout period. Sequencing the work — read the flash first, which does not need a halt, then halt briefly for the RAM — is the usual way to keep the window inside the budget.
The same problem in reverse: DMA
The cache problem has a mirror image that shows up during validation rather than acquisition. Code that writes a buffer and then hands it to a DMA engine must clean the cache first, or the DMA transfers the old contents. Firmware written for this family therefore contains cache maintenance calls scattered around every peripheral transfer, and their absence is itself informative.
A program that never touches the cache maintenance registers either runs with the cache disabled, which is common in bootloaders and in code that has been ported from an older part, or it does not use DMA at all. Both are visible in the image: the first from writes to the cache control block early in startup, the second from the absence of DMA stream configuration. When neither is present and the program clearly drives a peripheral at speed, the image is probably not the one that was running.
What belongs in the report
The deliverable should say which regions were captured with the core halted and which while running, whether cache maintenance was performed, and what the watchdog configuration was during the halt. It should also state the freshness caveat for any RAM region whose contents are used as evidence, because a customer who builds a behavioural specification from a stale state machine will not discover the discrepancy until the specification is already written.
For the memory layout that makes the same point from the other direction — a RAM map where the region bases do not follow the obvious pattern — STM32F765 heximal recovery covers the trap in detail. The path a device takes when it boots from its factory ROM rather than its own flash is in STM32F745 heximal extraction, and the external memory side of the same family, where the timing registers describe the attached part, is in STM32F769 program recovery. A worked case on a smaller member of the family with firmware encryption in play is at STM32F722RET6 firmware restoration.
FAQ
Does the cache affect a flash read? No. Flash is not cached in the data cache; the accelerator in front of it is a prefetch path, not a cache that can hold uncommitted writes.
Is halting the core enough to get a consistent view? Only if the cache is cleaned afterwards. Halting without cleaning leaves the dirty lines where they were, and the memory image stays old.
Can the cache be disabled to avoid the problem? It can, and some benches do, but disabling it changes execution timing enough to break devices with tight real-time loops, which defeats the purpose of reading a live system.
Conclusion
The single assumption underneath every extraction — that an address has a value — is true only once the writes have landed. On a part with a write-back data cache, the interval before they land is where inconsistent images come from, and the fix is procedural rather than technical: halt, clean, read, and state what was done. A recovery that reports a stable number without saying how it was obtained has reported a value, not a fact.