【问题标题】:Flush/Invalidate range by virtual address; ARMv8; Cache;通过虚拟地址刷新/无效范围; ARMv8;缓存;
【发布时间】:2017-06-24 04:48:46
【问题描述】:

我正在为在 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 被 set/way 结果刷新/无效时是正确的。

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

很快通过设置/方式刷新/无效工作正常。使用 VA 闪烁/无效也是如此。可能是什么问题?

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

【问题讨论】:

  • 如果范围/缓冲区很大,您可能应该只刷新整个 L1 缓存,然后暂停以确保在刷新 L2 缓存之前完成。此外,还有一个写缓冲区(或类似的)不是缓存的一部分。您不提供大小,也不提供“buf1”是否完全为零或部分。集合通常是连续的地址。
  • 缓冲区大小为 16KB。我也试过64B缓冲区,结果是一样的。通过 VA 刷新整个 L1 和刷新 L2 区域不起作用。在所有情况下,整个目标缓冲区都为零。
  • 对不起,我不熟悉 A53,但是在 Cortex-A7 上,有一个到 L2 的内存映射寄存器接口。 CP15 寄存器不会刷新 L2 高速缓存(即使手册可能表明这一点)。除了 Cortex-A53 TRM,你还有 ARM 手册吗?通常,SCU 和片上定时器等至少与早期的 ARM 芯片有单独的寄存器文件。 Linux 在cache.S 中使用了不同的机制。
  • @artlessnoise L2 Cache Controller 是 A7 中的独立芯片(与其他外围类似,即使它们都打包在一起)。 A53 集成了二级缓存。

标签: arm cpu-cache armv8


【解决方案1】:

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

来自 Cortex-A53 TRM

AArch32 中的 DCIMVAC 操作和 AArch64 中的 DC IVAC 指令执行目标地址无效。如果集群中的数据是脏的,那么在失效之前执行清理


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

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

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

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


解决方案/解决方法是

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


* 来自 'dst' 内存区域的数据可以提前提取到缓存中。如果在 DMA 将数据放入内存之前发生这种情况,则将使用缓存中的旧数据。第二次失效很好,因为数据没有被标记为“脏”,它将被执行为“纯失效”。在这种情况下无需清洁/冲洗。

【讨论】:

    猜你喜欢
    • 2016-07-10
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-09
    • 2016-04-29
    • 2013-01-22
    相关资源
    最近更新 更多