STM32 LCD Cache Coherency: Debug Stale Lines Without Disabling the Cache
STM32 LCD cache coherency guide: define buffer ownership, clean before DMA reads, invalidate after writes, and test cache-line partial updates.

STM32 LCD cache coherency faults often look like rendering bugs. An STM32 display that shows an old widget after a partial update, or a band that appears only when the cache is enabled, is often diagnosed by turning the cache off. That hides the fault and changes the timing. This guide defines buffer ownership across the render and flush stages, shows where cleaning and invalidation belong, and gives a test that separates a cache fault from a pixel-timing fault without sacrificing performance.
Where does STM32 LCD cache coherency become an issue?
Coherency starts when hardware reads memory the CPU wrote.
LTDC and DMA2D access framebuffer memory directly while the CPU may hold a newer cached copy, so the two views diverge without maintenance.
The LTDC reads the framebuffer through the bus, and DMA2D reads and writes its own source and destination surfaces. The CPU writes pixels through the cache. If the CPU’s write is still in a dirty cache line when the display engine reads memory, the engine sees the old data. If the display engine writes a surface and the CPU later reads a cached copy, the CPU sees stale data.
A write-back, write-allocate cache makes the problem more likely, because a CPU store may not reach memory until the line is evicted. The problem appears at the handover between the CPU and the hardware. It is not a general cache problem and it is not solved by making everything uncached. The driver needs to know who owns each buffer at each stage and perform the right maintenance at the handover.
Record the handover points in the flush path: after rendering, before the DMA starts, after the DMA completes and before the CPU reads the result. Each point has a different requirement.
Which buffers are read by hardware after CPU writes?
Any buffer the display engine reads after a CPU write.
The framebuffer, a DMA2D source or a descriptor table written by the CPU and read by hardware all need a clean before consumption.
The framebuffer is the obvious case, but it is not the only one. A DMA2D source image, a palette, a descriptor table or a command buffer written by the CPU and read by the DMA engine all need the same treatment. If the hardware reads a structure that the CPU wrote, the dirty cache lines must reach memory first.
The clean operation writes the dirty lines back to memory. It does not invalidate them, so the CPU can continue to use its cached copy. That is appropriate when the CPU is the producer and the hardware is the consumer.
A descriptor written with a normal store may sit in cache while the DMA reads an older copy from memory, which can start a transfer with the wrong address. List every buffer and descriptor the display path consumes, not only the visible framebuffer. A missing clean on a small descriptor can produce a failure that looks like a panel or DMA configuration problem.

How do cache-line boundaries affect partial updates?
Partial updates can share a cache line with other data.
A small rectangle may occupy part of a cache line, so maintenance on that line also affects neighbouring pixels or variables and can corrupt state.
A cache line is the unit of maintenance. If a small dirty rectangle occupies part of a line, cleaning the line writes the whole line, including neighbouring pixels that may belong to another surface or to non-display data. Invalidating the line discards the whole line, including any dirty data that was not written.
The hazard is greatest for small updates, which are exactly the case where partial refresh is used. A rectangle that starts in the middle of a line and ends before the next line boundary shares both lines with other data. The driver must either align the buffer to a line boundary and accept the padding, or use a maintenance strategy that accounts for the shared line.
If the buffer start is not aligned, the first and last lines of a region may contain pixels from another surface. Record the cache-line size and the alignment of every buffer. The ST AN4839 cache application note describes the clean and invalidate operations and the line behaviour for the named STM32 families.
When is cleaning needed and when is invalidation needed?
Clean before hardware reads; invalidate before CPU reads.
Cleaning writes dirty CPU data to memory for hardware, while invalidation discards cached data so the CPU sees what hardware wrote. Using the wrong one loses data.
Cleaning is used when the CPU has produced data and the hardware will consume it. The dirty lines are written to memory, and the cache retains a valid copy. Invalidation is used when the hardware has produced data and the CPU will consume it. The cached lines are discarded so the next CPU read fetches the new memory content.
Using the wrong operation is destructive. Invalidating a buffer that the CPU has just written discards the new pixels before they reach memory. Cleaning a buffer that the hardware has just written does nothing useful, because the CPU’s cached copy is already stale. The direction of the handover determines the operation.
A clean-and-invalidate operation is sometimes needed when ownership passes in both directions, but it is not a substitute for defining the handover. If a buffer is both read and written by both agents, the sequence needs more than one operation. Record the full sequence and the point at which each agent is allowed to touch the buffer.
How should buffer ownership move across render and flush stages?
Ownership moves at explicit clean and invalidate points.
Define producer and consumer for each buffer and place a clean or invalidate at the handover, rather than scattering calls through the render path.
The driver contract should name the owner of each buffer at each stage. During rendering, the CPU owns the buffer. At the flush, ownership passes to the display engine, and the driver cleans the buffer before starting the transfer. When the transfer completes, ownership returns to the CPU, and the driver invalidates if the CPU will read the result.
Scattering maintenance calls through the render path makes the contract invisible. A new code path can bypass a call and introduce a stale-data bug that appears only under a particular update pattern. A single handover function, called at the defined points, is easier to audit.
The handover function should also record a counter or trace point, so a missing call can be detected in a stress test. Record the owner and the operation in the driver documentation. The LVGL 9.2 display porting guide documents the flush and completion callbacks where the handover occurs.

Which test separates cache faults from pixel timing faults?
A known pattern separates stale data from timing errors.
Fill the buffer with a pattern, render a small change and compare panel with memory; a block holding an old value is coherency, a shifted block is timing.
Fill the framebuffer with a known pattern, render one small rectangle, and capture both the memory content and the panel image. If the panel shows a block with the old pattern where the new rectangle should be, the hardware read stale data. If the panel shows the new rectangle shifted or cropped, the fault is timing or addressing.
A logic analyzer on the memory bus can show whether the write reached memory before the read. Repeat with a pattern that changes every pixel, such as a moving bar. A coherency fault often affects a cache-line-sized block, while a timing fault follows the update boundary. The shape of the error is a clue.
Run the same test with the cache enabled and disabled, but use the cache-enabled result for the diagnosis. The disabled run can confirm that the fault disappears, but it cannot show whether the design is correct with the cache on.
What changes when buffers sit in non-cacheable memory?
Non-cacheable memory removes maintenance but slows the CPU.
A non-cacheable framebuffer avoids clean and invalidate calls but slows CPU access, which can shift the bottleneck to the bus and change the update rate.
A non-cacheable region is coherent by construction: every CPU access goes to memory, so the hardware sees the latest data without maintenance. That removes a class of bugs, but it also removes the cache’s performance benefit. CPU writes and reads become slower, and the bus load increases.
The trade-off may be acceptable for a small framebuffer or a low frame rate, but it can turn a bandwidth-limited design into a failing one. Measure the update rate and the CPU load with the buffer in each region before choosing.
The choice belongs in the memory map, not in a runtime flag. The LVGL display module documentation and the rotation documentation describe the buffer and update behaviour that the choice affects.
What coherency rules belong in the graphics driver contract?
The contract names who owns each buffer at each stage.
The contract should state buffer owner, maintenance operation, alignment rule and the flush-path point where it occurs, so a new path cannot bypass it.
The contract should list every buffer and descriptor used by the display path, its owner at each stage, the maintenance operation required at the handover and the alignment rule. It should state which buffers are cacheable and which are not, and why. It should also state the test that verifies the contract.
A review of any new buffer should ask who owns it, who maintains it and how the alignment is enforced. A new code path that allocates a buffer or starts a transfer must follow the same rules. If the contract is only in a comment, it will be bypassed. Put it in the driver interface and in the review checklist.
The table below is a template for the contract. It is not a set of limits; the values come from the STM32 cache and display documentation.
| Workload or state | Pixel or buffer format | Measured traffic or energy | Constraint | Evidence source |
|---|---|---|---|---|
| CPU renders to framebuffer | ARGB8888 | Dirty lines before flush | Cache-line alignment | Driver trace |
| LTDC reads framebuffer | Same storage format | Clean before scan | Owner handover | Pattern test |
| DMA2D writes destination | RGB565 | Invalidate before CPU read | Transfer completion | Pattern test |
| Descriptor table | Word-aligned structure | Clean before DMA | No shared cache line | Driver contract |
| Non-cacheable buffer | Same format | No maintenance | Slower CPU access | Performance measurement |
The ST AN4861 LTDC application note documents framebuffer access for specific STM32 families, and the display lifecycle guide covers the long-term change control that a driver contract should support.
FAQ
Can disabling cache conceal a bandwidth issue as well as a coherency issue?
Yes. Disabling the cache removes stale-data faults, but it also changes the memory traffic and the CPU timing. A design that only works with cache disabled may have a coherency bug, a bandwidth shortfall or both. Restore the cache and verify with a known pattern before accepting the result.
Should unrelated data share the same cache line as a DMA buffer?
It should not. If a variable shares a cache line with a buffer that is cleaned or invalidated, the maintenance operation can overwrite the variable or discard a recent update. Align and pad DMA buffers to a cache-line boundary so each line belongs to one owner.
Does a memory barrier replace a required cache maintenance operation?
No. A barrier orders accesses but does not write dirty data to memory or discard stale cache lines. The hardware can still read an old copy or the CPU can still read a cached copy. Use the barrier for ordering and the clean or invalidate for coherency; they solve different problems.
If a display shows stale or corrupted regions only with the cache enabled, send the buffer list, flush sequence and pattern captures to the CDTech engineering team for a coherency review.
Related articles
Touch Screen Grounding: Debug Floating and Earth-Referenced Installations SeparatelyCustom Displays, Sourcing & Manufacturing
Capacitive Touch Glove Thickness: Validate Material and Fit Before Setting SensitivityCustom Displays, Sourcing & Manufacturing
Capacitive Touch Water Rejection: Distinguish Droplets, Films and Flowing WaterCustom Displays, Sourcing & Manufacturing
Capacitive Touch Charger Noise: Correlate Raw Data with Power-Supply ConditionsCustom Displays, Sourcing & Manufacturing