Перейти к содержанию

Когерентность кэша

Ядро пишет буфер, запускает DMA — и в UART уходит мусор. Или DMA принял пакет, а программа читает старые данные. Виноват не DMA: данные застряли в кэше ядра или, наоборот, ядро читает свою старую копию. Это проблема когерентности кэша.

Обновлено Редакция GAW

Когерентность кэша (cache coherency) — согласованность данных в кэше процессора и в основной памяти, которую читают и пишут другие участники — прежде всего контроллер DMA. У микроконтроллеров с кэшем L1 — STM32F7 и STM32H7 на Cortex-M7 — аппаратной когерентности между кэшем данных ядра и DMA нет: её обеспечивает программа.

Две ситуации

  • Ядро пишет, DMA читает. При политике write-back запись ядра остаётся в кэше, строка помечается «грязной», а в память попадает только при вытеснении. DMA читает из памяти старые данные. Решение — clean: принудительно записать грязные строки буфера в память перед запуском DMA.
  • DMA пишет, ядро читает. DMA обновил память, а в кэше остались старые копии этих адресов. Ядро прочтёт их. Решение — invalidate: отбросить строки буфера из кэша перед чтением.

ST разбирает это в AN4839 на примере: DMA копирует массив из SRAM1 с атрибутом write-back в DTCM, и при включённом кэше данных результат не совпадает с ожидаемым.

Инструменты

СпособКакЦена
Обслуживание по адресуSCB_CleanDCache_by_Addr() перед передачей, SCB_InvalidateDCache_by_Addr() перед приёмомвремя операций, выравнивание буферов по 32 байта
Некэшируемая областьобласть буферов DMA через MPU с атрибутом shared или non-cacheableобращения ядра к буферам медленнее
Сквозная записьwrite-through для всей памятибольше записей на шину; invalidate перед чтением всё равно нужен
TCMбуферы в DTCM: она не кэшируется, а DMA обращается к ней через порт AHBS ядраобъём DTCM ограничен; у STM32H743/750 через AHBS работает только MDMA, DMA1, DMA2 и BDMA доступа к TCM не имеют (RM0433, табл. 3)

Операции по адресу в CMSIS работают строками по 32 байта, поэтому буфер DMA выравнивают по 32 байта и делают кратным 32 — иначе обслуживание затронет соседние переменные. По TRM Cortex-M7 операции обслуживания кэша, как и инструкции DMB и DSB, сливают буфер записи; перед запуском DMA после clean ставят барьер памяти DSB, а функции CMSIS содержат его сами. Настройка потоков DMA в STM32 — на странице «DMA в STM32».

Частые вопросы

Когда делать clean, а когда invalidate?

Clean — перед тем как DMA прочитает буфер, который заполнило ядро: грязные строки кэша записываются в память. Invalidate — перед тем как ядро прочитает буфер, который заполнил DMA: устаревшие копии в кэше отбрасываются, и следующее чтение пойдёт в память. Так сформулировано и в разделе ошибок AN4839.

Почему буфер DMA выравнивают по 32 байта?

Потому что кэш работает строками по 32 байта: операции по адресу в CMSIS идут с 32-байтно выровненного адреса с шагом 32 байта. Если буфер делит строку с другой переменной, invalidate буфера сотрёт её свежее значение, а clean соседней переменной перезапишет данные DMA. Поэтому буфер выравнивают по 32 байта и делают кратным 32.

Можно ли просто не кэшировать буферы DMA?

Да, и AN4839 это предлагает: выделить некэшируемую область — через MPU атрибутом shared или non-cacheable — и класть туда буферы обмена с DMA. Ещё один вариант из того же документа — сквозная запись для всей памяти, тогда clean не нужен, но invalidate перед чтением остаётся.

Источники

  1. STMicroelectronics. AN4839 Level 1 cache on STM32F7 Series and STM32H7 Series, Rev 2 (March 2018) — введение: кэшируемые системы должны обеспечивать когерентность данных между ядром и основной памятью при работе с общими данными; табл. 1: clean записывает грязные строки в память (иногда называется flush), invalidate отбрасывает содержимое; разд. 3.2: пример — DMA копирует из SRAM1 с атрибутом write-back в DTCM, при включённом D-cache обнаруживается расхождение данных; решения: 1 — SCB_CleanDCache() после записи в кэшируемую область, 2 — атрибут shared для SRAM1 через MPU, 3 — принудительная сквозная запись; перед чтением обновлённых DMA данных — invalidate; разд. 4: перед запуском DMA — clean, перед чтением результата DMA — invalidate, либо некэшируемая область для обмена с DMA
  2. Arm. CMSIS 5.9.0, CMSIS/Core/Include/cachel1_armv7.h (V1.0.1, 19 April 2021) — SCB_CleanDCache_by_Addr, SCB_InvalidateDCache_by_Addr, SCB_CleanInvalidateDCache_by_Addr(addr, dsize): операция идёт с 32-байтно выровненного адреса с шагом 32 байта, регистры DCCMVAC и DCIMVAC принимают только биты 31..5 адреса; цикл операций обрамлён __DSB() и __ISB()
  3. Arm. Cortex-M7 Processor Technical Reference Manual r1p2 (DDI 0489F), разд. «Cache maintenance operations», «Store buffer», «Memory types and memory system behavior» — операции с кэшем данных: invalidate, clean, clean and invalidate — по адресу или по set/way; с кэшем команд: invalidate all и по адресу; выполняются записью в регистры SCS; буфер записи полностью сливается при операциях обслуживания кэша и инструкциях DMB и DSB; по умолчанию кэшируется только Non-shareable Normal память, Shared — только при CACR.SIWT = 1; доступы к TCM и AHBP идут мимо кэша; интерфейс AHBS даёт DMA доступ к TCM
  4. STMicroelectronics. RM0433 Reference manual STM32H742, STM32H743/753 and STM32H750 Value line, Rev 9, разд. 2.1, табл. 3, рис. 1 — табл. 3: к ITCM и DTCM из ведущих имеют доступ только ядро (шины ITCM и DTCM) и MDMA через AHBS, у DMA1, DMA2, BDMA, DMA2D, LTDC, SDMMC, Ethernet и USB доступа нет; разд. 2.1.2: DTCM и ITCM подключены к Cortex-M7 отдельными TCM-шинами, MDMA обращается к ним через AHBS — ведомый AHB-порт ядра; ITCM работает на частоте ядра без тактов ожидания; разд. 2.1.5: AXIM (64 бит) — доступ ко всей памяти, кроме ITCM и DTCM, и к периферии AHB3, AHB4, APB3, APB4; DTCM — шина 2 × 32 бит; AHBS (32 бит) — только для MDMA

Нашли ошибку?