cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 Ethernet + DMA + Cache Explained + LwIP +Without Any OS, This explanation to help save your time.

Ahmet Yasin CİVAN
Associate III

STM32H7 Ethernet + DMA + Cache Explained + LwIP (EN) 

One of the most common sources of confusion when working with STM32H7 microcontrollers and Ethernet (via LwIP + HAL ETH) is data cache (D-Cache) behavior.

When using DMA (like Ethernet MAC DMA), if D-Cache is enabled (which it is by default in STM32H7 projects), you must manage cache coherency manually. Otherwise, the DMA peripheral and the CPU will not agree on what data is in RAM.

I encountered this exact issue myself and spent significant time identifying the root cause. Since many others using STM32H7 are likely to face this same problem, I'm sharing this explanation to help save your time and effort.

:bar_chart: Problem:

If you fill a pbuf->payload buffer with data and send it using HAL_ETH_Transmit() without cleaning the D-Cache, the CPU will still be holding the most recent data in its cache, but the DMA will read stale or uninitialized data from RAM.

Result: corrupted Ethernet frames, broken ping replies, UDP issues, and silent communication errors.

:wrench: Solution: Clean the D-Cache

Before calling HAL_ETH_Transmit(), you must flush the cache so the DMA can see the actual up-to-date data. This should be done inside low_level_output() function, typically located in ethernetif.c. If CubeMX overwrites this file, you can wrap it by renaming the original to low_level_output_internal() and calling it from a user-defined low_level_output() placed in a separate file such as ethernetif_user.c.

:warning: This cache clean step is necessary whether or not you use an RTOS. In FreeRTOS-based projects, the D-Cache is still active and must be manually synchronized before DMA transactions, just as in bare-metal applications.

uint32_t addr = (uint32_t)p->payload;
uint32_t size = p->len;
uint32_t aligned_addr = addr & ~0x1F;
uint32_t aligned_size = ((addr + size + 31) & ~0x1F) - aligned_addr;
SCB_CleanDCache_by_Addr((uint32_t*)aligned_addr, aligned_size);

HAL_ETH_Transmit(&heth, &TxConfig, ETH_DMA_TRANSMIT_TIMEOUT);

:construction: Summary: When to use which?

Direction Cache Operation

CPU ➔ DMA (Tx)SCB_CleanDCache_by_Addr()
DMA ➔ CPU (Rx)SCB_InvalidateDCache_by_Addr()

STM32H7 Ethernet + DMA + Cache Anlatımı + LwIP (TR)

STM32H7 mikrodenetleyicilerinde Ethernet (LwIP + HAL ETH) kullanırken karşılaşılan en kritik sorunlardan biri data cache (D-Cache) ile DMA arasındaki uyumsuzluktur.

D-Cache etkinse (STM32H7 projelerinde genelde aktiftir), DMA ile çalışırken cache senkronizasyonunu manuel olarak yapman gerekir. Aksi takdirde DMA RAM’den eski veriyi okurken CPU cache'te yeni veriyi görür.

Ben de bu sorunla bizzat karşılaştım ve çözümü bulmam zaman aldı. Aynı sıkıntıyı yaşayabilecek başka geliştiricilere yol göstermesi için bu yazıyı paylaşıyorum.

:bar_chart: Sorun:

p->payload içine veri yazdın ve HAL_ETH_Transmit() ile DMA'ya verdin ama SCB_CleanDCache_by_Addr() çağrılmadıysa:

  • CPU veriyi sadece cache’e yazmıştır

  • DMA RAM’den eski ya da boş veri alır

Sonuç: bozuk ping cevabı, boş UDP paketleri, sessiz haberleşme hataları

:wrench: Çözüm: Cache Temizleme

DMA gönderimi öncesi cache’i temizlemelisin. Bu kod parçası ethernetif.c dosyasındaki low_level_output() fonksiyonuna eklenmelidir. Ancak CubeMX bu dosyayı sık sık yenilediği için, fonksiyonu low_level_output_internal() olarak değiştirip, gerçek low_level_output() fonksiyonunu ethernetif_user.c gibi bir kullanıcı dosyasına taşıman tavsiye edilir:

:warning: Bu temizleme adımı, RTOS kullanılıp kullanılmamasından bağımsız olarak gereklidir. FreeRTOS tabanlı projelerde de D-Cache aktiftir ve DMA işlemlerinden önce elle senkronizasyon gerekir.

uint32_t addr = (uint32_t)p->payload;
uint32_t size = p->len;
uint32_t aligned_addr = addr & ~0x1F;
uint32_t aligned_size = ((addr + size + 31) & ~0x1F) - aligned_addr;
SCB_CleanDCache_by_Addr((uint32_t*)aligned_addr, aligned_size);

HAL_ETH_Transmit(&heth, &TxConfig, ETH_DMA_TRANSMIT_TIMEOUT);

:construction: Özet: Ne zaman hangisini kullanmalısın?

Veri Yönü Yapılacak İşlem

CPU ➔ DMA (Tx)SCB_CleanDCache_by_Addr()
DMA ➔ CPU (Rx)SCB_InvalidateDCache_by_Addr()

Bu basit ama hayati detay, STM32H7 ile Ethernet haberleşmesinin sorunsuz çalışması için şarttır.



How to create a project for STM32H7 with Ethernet and LwIP stack working 

STM32H7 Series, Ethernet, LwIP, D-Cache, RAM, DMA

0 REPLIES 0