Flush/Invalidate 虚拟地址范围; ARMv8;缓存;

Flush/Invalidate range by virtual address; ARMv8; Cache;

我正在 32 位模式下为 ARMv8 (Cortex-A53) 运行 实现缓存维护功能。 当我尝试使用虚拟地址 (VA) 刷新内存区域时出现问题。 DCacheFlushByRange 看起来像这样

// some init.
// kDCacheL1 = 0; kDCacheL2 = 2;
while (alignedVirtAddr < endAddr)
{
    // Flushing L1
    asm volatile("mcr   p15, 2, %0,  c0,  c0,  0" : : "r"(kDCacheL1) :);        // select cache
    isb();
    asm volatile("mcr   p15, 0, %0,  c7, c14,  1" : : "r"(alignedVirtAddr) :);  // clean & invalidate
    dsb();

    // Flushing L2
    asm volatile("mcr   p15, 2, %0,  c0,  c0,  0" : : "r"(kDCacheL2) :);        // select cache
    isb();
    asm volatile("mcr   p15, 0, %0,  c7, c14,  1" : : "r"(alignedVirtAddr) :);  // clean & invalidate
    dsb();

    alignedVirtAddr += lineSize;
}

DMA 用于验证功能。 DMA 将一个缓冲区复制到另一个缓冲区。源缓冲区在 DMA 之前被刷新,目标缓冲区在 DMA 完成后无效。缓冲区是 64 字节对齐的。测试

for (uint32_t i = 0; i < kBufSize; i++)
    buf1[i] = 0;
for (uint32_t i = 0; i < kBufSize; i++)
    buf0[i] = kRefValue;

DCacheFlushByRange(buf0, sizeof(buf0));

// run DMA
while (1) // wait DMA completion;

DCacheInvalidateByRange(buf1, sizeof(buf1));
compare(buf0, buf1);

在转储中我可以看到 buf1 仍然只包含零。当缓存关闭时,结果是正确的,因此 DMA 本身可以正常工作。

另外一点是当整个 D-cache flushed/invalidated by set/way 结果是正确的。

// loops th/ way & set for L1 & L2
asm volatile("mcr   p15, 0, %0,  c7, c14,  2" : : "r"(setway) :)

很快 flush/invalidate by set/way 工作正常。 flashing/invalidating 使用 VA 则不会。可能是什么问题?

PS:kBufSize=4096;,总缓冲区大小为 4096 * sizeof(uint32_t) == 16KB

函数本身没有问题,而不是 Cortex-A53 缓存实现功能。

来自 Cortex-A53 TRM

DCIMVAC operations in AArch32 and DC IVAC instructions in AArch64 perform an invalidate of the target address. If the data is dirty within the cluster then a clean is performed before the invalidate.


所以没有实际的无效,有 clean 和 invalidate

正常(至少对我而言)顺序是

flush(src);
dma(); // copy src -> dst
invalidate(dst);

但由于 invalidate() 会刷新,缓存(dst 区域)中的旧数据在 DMA 传输后写入内存中的数据之上。


Solution/workaround 是

flush(src);
invalidate(dst);
dma(); // copy src -> dst
invalidate(dst); // again, that's right*.


* 来自'dst' 内存区域的数据可以被提前提取到缓存中。如果在 DMA 将数据放入内存之前发生这种情况,则会使用缓存中的旧数据。第二次 invalidate 没问题,因为数据没有被标记为 'dirty',它将被执行为 'pure invalidate'。在这种情况下没有 clean/flush。