Когерентность кэша
Ядро пишет буфер, запускает 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 перед чтением остаётся.
Источники
- 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
- 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()
- 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
- 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
Нашли ошибку?
Смотрите также
- Кэш L1 Cortex-M7 (I-cache, D-cache)Раздельные кэши команд и данных ядра Cortex-M7: строка 32 байта
- Политика записи кэша (write-through, write-back)Сквозная и отложенная запись кэша данных и выделение строки при записи
- DMAПрямой доступ к памяти без участия ядра
- DMA STM32Каналы и потоки, запросы периферии, кольцо, двойной буфер, FIFO, флаги, примеры
- Блок защиты памяти (MPU)Права доступа к областям памяти в Cortex-M
- Барьер памятиГраница, через которую нельзя переставлять обращения к памяти