repstosw 18 21 апреля, 2022 Опубликовано 21 апреля, 2022 (изменено) · Жалоба Дано: - ядро ARM32 Cortex-A7 - включенный MMU (физический адрес = виртуальный адрес) с проставленными битами кеширования(C=1) и буферизацией (B=1) - графический ускоритель, который: читает данные из региона памяти или записывает данные в регион памяти Мы знаем, что перед тем как периферал (DMA, упомянутый графический ускоритель, кодек,...) начнёт читать память, нужно сбросить содержимое кеша данных в память. Потому что перифералы не работают с кешем, а работают с памятью напрямую. И наоборот, после того как периферал сбросит в память данные, нужно объявить кеш данных недостоверным(инвалидация кеша) перед использованием этих данных процессором(CPU). Потому что процессор читает данные через кеш, о дополнительных изменениях памяти перифералом ни он, ни кеш не знают. Вроде ничего сложного. Но.... В интернете я нашёл несколько вариаций на тему "манипуляции с кешем данных". Вот они: 1) /* c code declared as follows: int flush_clean_user_range(long start, long end); */ .text .globl flush_clean_user_range flush_clean_user_range: mrc p15, 1, r3, c0, c0, 0 @ read CSIDR and r3, r3, #7 @ cache line size encoding mov r2, #16 @ size offset mov r2, r2, lsl r3 @ actual cache line size sub r3, r2, #1 bic r0, r0, r3 1: mcr p15, 0, r0, c7, c14, 1 @ clean and flush D line to the point of unification add r0, r0, r2 2: cmp r0, r1 blo 1b mov r0, #0 dsb mov pc, lr /* * Fault handling for the cache operation above. If the virtual address in r0 * isn't mapped, just try the next page. */ 9001: mov r0, r0, lsr #12 mov r0, r0, lsl #12 add r0, r0, #4096 b 2b 2) #define CACHE_LINE_SIZE 32 void mmu_clean_dcache(uint32_t buffer,uint32_t size) { unsigned int ptr; ptr = buffer & ~(CACHE_LINE_SIZE - 1); while (ptr < buffer + size) { __asm__ __volatile__ ( "mcr p15, 0, %0, c7, c10, 1" : "=r" (ptr) ); ptr += CACHE_LINE_SIZE; } } 3) #define CACHE_LINE_SIZE 32 void mmu_invalidate_dcache(uint32_t buffer,uint32_t size) { unsigned int ptr; ptr = buffer & ~(CACHE_LINE_SIZE - 1); while (ptr < buffer + size) { __asm__ __volatile__ ( "mcr p15, 0, %0, c7, c6, 1" : "=r" (ptr) ); ptr += CACHE_LINE_SIZE; } } 4) /* ;******************************************************************************* ;єЇКэГыіЖ: flush_dcache ;єЇКэФРН: void flush_dcache( void ) ;єЇКэ№¦ДЬ: flush data cache ;ИлїЪІОКэ: void ;·µ »Ш Цµ: void ;±ё Чў: ;******************************************************************************* */ .globl flush_dcache flush_dcache: STMFD sp!, {r0-r12, lr} MRC p15, 1, r0, c0, c0, 1 @; read clidr ANDS r3, r0, #0x7000000 @; extract loc from clidr MOV r3, r3, lsr #23 @; left align loc bit field BEQ finished @; if loc is 0, then no need to clean mov r10, #0 @; start clean at cache level 0 loop1: ADD r2, r10, r10, lsr #1 @; work out 3x current cache level MOV r1, r0, lsr r2 @; extract cache type bits from clidr AND r1, r1, #7 @; mask of the bits for current cache only CMP r1, #2 @; see what cache we have at this level BLT skip @; skip if no cache, or just i-cache MCR p15, 2, r10, c0, c0, 0 @; select current cache level in cssr ISB @; isb to sych the new cssr&csidr MRC p15, 1, r1, c0, c0, 0 @; read the new csidr AND r2, r1, #7 @; extract the length of the cache lines ADD r2, r2, #4 @; add 4 (line length offset) LDR r4, =0x3ff ANDS r4, r4, r1, lsr #3 @; find maximum number on the way size CLZ r5, r4 @; find bit position of way size increment LDR r7, =0x7fff ANDS r7, r7, r1, lsr #13 @; extract max number of the index size loop2: MOV r9, r4 @; create working copy of max way size loop3: ORR r11, r10, r9, lsl r5 @; factor way and cache number into r11 ORR r11, r11, r7, lsl r2 @; factor index number into r11 MCR p15, 0, r11, c7, c14, 2 @; clean & invalidate by set/way SUBS r9, r9, #1 @; decrement the way BGE loop3 SUBS r7, r7, #1 @; decrement the index BGE loop2 skip: ADD r10, r10, #2 @; increment cache number CMP r3, r10 BGT loop1 finished: MOV r10, #0 @; swith back to cache level 0 MCR p15, 2, r10, c0, c0, 0 @; select current cache level in cssr ISB LDMFD sp!, {r0-r12, lr} MOV pc, lr /* Иногда вместо 2) и 3) - делают везде 1). Почему? Какие из вариантов будут быстрее работать? А какие медленно? Что конкретно делает каждый из вариантов? Изменено 21 апреля, 2022 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 22 апреля, 2022 Опубликовано 22 апреля, 2022 · Жалоба Что, никто не знает ответов на вопросы? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AleksBak 0 22 апреля, 2022 Опубликовано 22 апреля, 2022 · Жалоба 23 часа назад, repstosw сказал: В интернете я нашёл несколько вариаций на тему "манипуляции с кешем данных" А "родную" (CMSIS-Core (Cortex-A) - наверное) библиотеку нельзя использовать? Обычно, хорошие люди, ее используют. А "что быстрее" тут вроде бы не так уж и сложно определить при желании - внимательно все операции по шагам просмотреть/проверить. Но только есть ли в этом какой-то сокровенный смысл? (если посмотреть, то некоторые варианты тут почти не отличаются даже). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 141 22 апреля, 2022 Опубликовано 22 апреля, 2022 · Жалоба Надергали не пойми откуда примеров. Полагаю, что 1-й и (2-й + 3-й) вовсе из разных проектов. Какой из них правильный? Да все, скорее, всего, правильные. Но каноничные варианты те, где раздельны flush/clean и invalidate. Что делает 1-й я не знаю - по описанию лишь flush-ит. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 22 апреля, 2022 Опубликовано 22 апреля, 2022 · Жалоба 1 hour ago, Arlleex said: Надергали не пойми откуда примеров. Полагаю, что 1-й и (2-й + 3-й) вовсе из разных проектов. Какой из них правильный? Да все, скорее, всего, правильные. Но каноничные варианты те, где раздельны flush/clean и invalidate. Что делает 1-й я не знаю - по описанию лишь flush-ит. Мощно! Тогда чем FLUSH от CLEAN отличаются? FLUSH и CLEAN могут заменить INVALIDATE? Повторюсь, в примерах видел что.... On 4/21/2022 at 7:01 PM, repstosw said: Иногда вместо 2) и 3) - делают везде 1). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
GenaSPB 11 22 апреля, 2022 Опубликовано 22 апреля, 2022 (изменено) · Жалоба Я в описанных случаях могу посоветовать ПЕРЕД чтением по DMA делать CleanInvalidate нужной области (или Invalidate если нет пересечений с чем-то еще) Перед записью (когда периферия будет использовать буфер) после формирования данных - Clean. Invalidate - если нет пересечений с другими буферами и хотите оптимизировать скорость (т.е. если потом чтений процессор не будет выполнять из этой области). Есть еще экзотические случаи - например проверяем статусы буферов EHCI - проверили и сделали invalidate - чтобы потом новое содержимое флагов иметь возможность проверить. Для ясности могу посоветовать использовать CMSIS библиотеку - её функции Spoiler // MVA // For more information about the possible meaning when the table shows that an MVA is required // see Terms used in describing the maintenance operations on page B2-1272. // When the data is stated to be an MVA, it does not have to be cache line aligned. __STATIC_FORCEINLINE void L1_CleanDCache_by_Addr(volatile void *addr, int32_t dsize) { if (dsize > 0) { int32_t op_size = dsize + (((uintptr_t) addr) & (DCACHEROWSIZE - 1U)); uintptr_t op_mva = (uintptr_t) addr; __DSB(); do { __set_DCCMVAC(op_mva); // Clean data cache line by address. op_mva += DCACHEROWSIZE; op_size -= DCACHEROWSIZE; } while (op_size > 0); __DMB(); // ensure the ordering of data cache maintenance operations and their effects } } __STATIC_FORCEINLINE void L1_CleanInvalidateDCache_by_Addr(volatile void *addr, int32_t dsize) { if (dsize > 0) { int32_t op_size = dsize + (((uintptr_t) addr) & (DCACHEROWSIZE - 1U)); uintptr_t op_mva = (uintptr_t) addr; __DSB(); do { __set_DCCIMVAC(op_mva); // Clean and Invalidate data cache by address. op_mva += DCACHEROWSIZE; op_size -= DCACHEROWSIZE; } while (op_size > 0); __DMB(); // ensure the ordering of data cache maintenance operations and their effects } } __STATIC_FORCEINLINE void L1_InvalidateDCache_by_Addr(volatile void *addr, int32_t dsize) { if (dsize > 0) { int32_t op_size = dsize + (((uintptr_t) addr) & (DCACHEROWSIZE - 1U)); uintptr_t op_mva = (uintptr_t) addr; do { __set_DCIMVAC(op_mva); // Invalidate data cache line by address. op_mva += DCACHEROWSIZE; op_size -= DCACHEROWSIZE; } while (op_size > 0); // Cache Invalidate operation is not follow by memory-writes __DMB(); // ensure the ordering of data cache maintenance operations and their effects } } #if (__L2C_PRESENT == 1) __STATIC_FORCEINLINE void L2_CleanDCache_by_Addr(volatile void *addr, int32_t dsize) { if (dsize > 0) { int32_t op_size = dsize + (((uint32_t) addr) & (DCACHEROWSIZE - 1U)); uint32_t op_addr = (uint32_t) addr /* & ~(DCACHEROWSIZE - 1U) */; do { // Clean cache by physical address L2C_310->CLEAN_LINE_PA = op_addr; // Atomic operation. These operations stall the slave ports until they are complete. op_addr += DCACHEROWSIZE; op_size -= DCACHEROWSIZE; } while (op_size > 0); } } __STATIC_FORCEINLINE void L2_CleanInvalidateDCache_by_Addr(volatile void *addr, int32_t dsize) { if (dsize > 0) { int32_t op_size = dsize + (((uint32_t) addr) & (DCACHEROWSIZE - 1U)); uint32_t op_addr = (uint32_t) addr /* & ~(DCACHEROWSIZE - 1U) */; do { // Clean and Invalidate cache by physical address L2C_310->CLEAN_INV_LINE_PA = op_addr; // Atomic operation. These operations stall the slave ports until they are complete. op_addr += DCACHEROWSIZE; op_size -= DCACHEROWSIZE; } while (op_size > 0); } } __STATIC_FORCEINLINE void L2_InvalidateDCache_by_Addr(volatile void *addr, int32_t dsize) { if (dsize > 0) { int32_t op_size = dsize + (((uint32_t) addr) & (DCACHEROWSIZE - 1U)); uint32_t op_addr = (uint32_t) addr /* & ~(DCACHEROWSIZE - 1U) */; do { // Invalidate cache by physical address L2C_310->INV_LINE_PA = op_addr; // Atomic operation. These operations stall the slave ports until they are complete. op_addr += DCACHEROWSIZE; op_size -= DCACHEROWSIZE; } while (op_size > 0); } } #endif /* (__L2C_PRESENT == 1) */ // Записать содержимое кэша данных в память // применяетмся после начальной инициализации среды выполнния void FLASHMEMINITFUNC arm_hardware_flush_all(void) { L1C_CleanInvalidateDCacheAll(); #if (__L2C_PRESENT == 1) L2C_CleanInvAllByWay(); #endif } // Сейчас в эту память будем читать по DMA void arm_hardware_invalidate(uintptr_t addr, int_fast32_t dsize) { L1_InvalidateDCache_by_Addr((void *) addr, dsize); #if (__L2C_PRESENT == 1) L2_InvalidateDCache_by_Addr((void *) addr, dsize); #endif /* (__L2C_PRESENT == 1) */ } // Сейчас эта память будет записываться по DMA куда-то void arm_hardware_flush(uintptr_t addr, int_fast32_t dsize) { L1_CleanDCache_by_Addr((void *) addr, dsize); #if (__L2C_PRESENT == 1) L2_CleanDCache_by_Addr((void *) addr, dsize); #endif /* (__L2C_PRESENT == 1) */ } // Сейчас эта память будет записываться по DMA куда-то. Потом содержимое не требуется void arm_hardware_flush_invalidate(uintptr_t addr, int_fast32_t dsize) { L1_CleanInvalidateDCache_by_Addr((void *) addr, dsize); #if (__L2C_PRESENT == 1) L2_CleanInvalidateDCache_by_Addr((void *) addr, dsize); #endif /* (__L2C_PRESENT == 1) */ } Лежит тут:https://github.com/ua1arn/hftrx/blob/08911deb021d612b234b6a691a83de508a137adc/src/hardware.c#L2018 On 4/21/2022 at 12:01 PM, repstosw said: - графический ускоритель, который: читает данные из региона памяти или записывает данные в регион памяти Из любопытства спрошу - что за процессор, от граф сопроцессора которого нашли описание? Изменено 22 апреля, 2022 пользователем GenaSPB Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 141 22 апреля, 2022 Опубликовано 22 апреля, 2022 · Жалоба 10 минут назад, repstosw сказал: Тогда чем FLUSH от CLEAN отличаются? Буквами в названии, видимо. Вот, например. Цитата FLUSH и CLEAN могут заменить INVALIDATE? Разумеется, нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 22 апреля, 2022 Опубликовано 22 апреля, 2022 · Жалоба 5 hours ago, GenaSPB said: Из любопытства спрошу - что за процессор, от граф сопроцессора которого нашли описание? Это был просто пример. Из последнего - расковырял JPEG-кодек Cedar для Allwinner A13. Он и читает и пишет в регионы памяти. Поэтому снова стал актуален вопрос связанный с кешированием. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
strannyi 4 25 мая, 2022 Опубликовано 25 мая, 2022 · Жалоба А кеш то какой? Write-through? Write-back? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться