🛠 TCM, DMA et cache sur le STM32H743ZI : placer ses données pour un temps réel déterministe
What you will build
The H743ZI is a memory engineer's MCU: 192 KB of TCM RAM (tightly-coupled, deterministic single-cycle access) plus up to 864 KB user SRAM and 4 KB backup SRAM, wired by an AXI/MBX interconnect, with a 16-channel DMA engine and a Cortex-M7 that caches aggressively. This tutorial places code and data on purpose instead of by accident — the difference between a deterministic control loop and a heisenbug.
The rules of the house
- TCM for what must never miss: ISRs, time-critical loops and task stacks placed in TCM get deterministic timing — the board sheet calls out TCM RAM for deterministic real-time execution as a headline feature.
- DMA buffers must be cache-safe: with the M7 data cache on, a buffer in cacheable SRAM can be read stale by the CPU or overwritten by the DMA before eviction. Either place buffers in non-cacheable MPU regions, or manage maintenance explicitly.
- The errata are real: ST's errata sheet ES0392 (H742/H743/H750/H753) must be read before using DMA/cache-heavy or Ethernet corner cases.
- Benchmark only after checking clocks: Zephyr boots at 96 MHz, the STM32duino core at 480 MHz (VOS0, AHB at HCLK/2 = 240 MHz). Same silicon, 5× the score — clock first, numbers second.
Step 1 — Place things explicitly
/* GCC linker-section attributes (linker script must define the regions). */
__attribute__((section(".dtcmram")))
volatile uint32_t ctrl_state; /* hot state in TCM: single-cycle */
__attribute__((section(".dma_buffers"))) /* MPU-marked Non-cacheable */
uint8_t spi_dma_buf[4096];
void __attribute__((section(".itcmram"), optimize("Ofast")))
fast_control_loop(void) { /* time-critical loop out of flash */
for (uint32_t i = 0; i < 64; i++) acc += k[i];
}
Step 2 — Prove determinism
- Run the control loop from flash with cache on, toggle a GPIO per iteration, measure jitter on a scope.
- Move the loop to ITCM and the data to DTCM ; re-measure — the jitter band collapses because neither fetch nor load crosses the cached AXI path.
- Add a DMA stream hammering the AXI/MBX interconnect: the flash-cached version degrades, the TCM version does not.
Step 3 — Cache maintenance when you cannot move the buffer
/* Clean before DMA transmit, invalidate before CPU read after receive. */
SCB_CleanDCache_by_Addr(spi_dma_buf, sizeof spi_dma_buf);
HAL_SPI_Transmit_DMA(&hspi1, spi_dma_buf, len);
/* ... on transfer-complete callback: */
SCB_InvalidateDCache_by_Addr(spi_dma_buf, sizeof spi_dma_buf);
Keep buffers 32-byte aligned and padded to cache-line size so neighbouring variables never share a line with a DMA buffer.
Expected result
A control path whose worst-case timing does not move when the rest of the system gets busier — plus a written map of which regions your linker script declares cacheable or not.
Common pitfalls
- Sharing a cache line between a DMA buffer and hot variables: corrupted data that vanishes when you add a
printf. - Forgetting the RNG peripheral exists when you need entropy: the H743 ships a True Random Number Generator — use it instead of software PRNGs (crypto acceleration beyond RNG is not confirmed for the H743).
- Trusting benchmark posts: without the clock state (96 / 400 / 480 MHz) a number is meaningless on this family.
Autres tutoriels pour cette carte
- Premiers pas sur le Nucleo-144 STM32H743ZI : core STM32duino, ST-LINK et premier Blink
- GPIO, ADC 16 bits et DAC sur le Nucleo STM32H743ZI : bouton, LED et potentiomètre
- MicroPython sur le Nucleo STM32H743ZI : le REPL 480 MHz via le port série ST-LINK
- Zephyr RTOS sur le Nucleo STM32H743ZI : west build, flash et le piège des 96 MHz
- STM32Cube HAL à 480 MHz sur le STM32H743ZI : VOS0, latence flash et HSE bypass
- FreeRTOS sur le Nucleo STM32H743ZI : le projet officiel CubeH7, tâches et file d'attente