使数据缓存的特定区域无效而不刷新其内容
Posted
技术标签:
【中文标题】使数据缓存的特定区域无效而不刷新其内容【英文标题】:Invalidating a specific area of data cache without flushing its content 【发布时间】:2021-10-27 03:50:03 【问题描述】:我目前正在使用 Zynq-7000 SoC 开展一个项目。我们在 PL 中有一个定制的 DMA IP,以在外设和主存储器之间提供更快的事务。外设一般是串口设备,如UART。串行设备接收到的数据立即通过 DMA 传输到主存储器。
我尝试做的是访问存储在内存预定位置的数据。在读取数据之前,我使用xil_cache.h
library提供的函数使相关缓存行失效,如下所示。
Xil_DCacheInvalidateRange(INTPTR adr, u32 len);
这里的问题是这个函数在使它们失效之前刷新相关的缓存行。由于刷新,存储的数据被覆盖。因此,每次我获取损坏的字节时。该过程已在以下库文档中进行了说明。
如果要失效的地址不是缓存行对齐的,则 有以下选择:
当缓存行无效时 需要并且不要为副作用而烦恼。虽然听起来 很好,它可能会导致难以调试的问题。问题是,如果一些 其他变量分配在同一缓存行中,并且已 最近更新(在缓存中),失效将导致丢失 数据。 首先刷新缓存行。这将确保如果有 其他变量出现在同一缓存行中并最近更新是 冲出记忆。然后它可以安全地失效。再次它 听起来不错,但这可能会导致问题。例如,当 失效发生在典型的 ISR 中(在 DMA 传输之后 更新了内存),然后刷新缓存行意味着,丢失数据 是在 ISR 被调用之前最近更新的。
您可以猜到,我不能总是分配具有高速缓存行对齐地址的内存区域。因此,我采用不同的方法来解决问题,以便计算缓存行对齐的地址,该地址位于缓冲区之前的内存中。然后我用那个地址调用失效方法。请注意,Zynq 的 L2 缓存是一个 8 路组关联 512KB 缓存,具有固定的 32 字节行大小。这就是我屏蔽给定内存地址的最后 5 位的原因。 (查看第 3.4 节:Zynq's documentation 中的二级缓存)
INTPTR invalidationStartAddress = INTPTR(uint32_t(dev2memBuffer) - (uint32_t(dev2memBuffer) & 0x1F));
Xil_DCacheInvalidateRange(invalidationStartAddress, BUFFER_LENGTH);
这样我可以解决问题,但我不确定我是否违反了放置在为 DMA 分配的资源之前放置的任何资源。(我想补充一点,引用的资源已分配在堆使用动态分配运算符new
。) 有没有办法克服这个问题,还是我想多了?我相信如果有一个函数可以使相关的缓存行无效而不刷新它们,我相信这个问题可以得到更好的解决。
编辑:使不在分配区域内的资源无效违反了放置在引用资源附近的变量的可靠性。因此,第一个解决方案不适用。我的第二个解决方案是分配一个比所需缓冲区大 32 字节的缓冲区并裁剪其未对齐的部分。但是,这也可能导致与最后一部分相同的问题*(部分 = 32 字节块)* 不保证有 32 字节。因此,它可能会破坏放置在它旁边的资源。图书馆文档指出:
只要有可能,地址必须与高速缓存行对齐。请 注意不仅是起始地址,连结束地址也必须是 缓存行对齐。如果解决了这个问题,这将始终有效。
解决方案: 正如我在上次编辑中所述,解决该问题的唯一方法是分配一个具有缓存对齐地址和长度的内存区域。我无法确定分配区域的起始地址,因此我决定分配一个比请求的缓存块大两个缓存块的空间并裁剪未对齐的部分。未对齐可以发生在第一个或最后一个块。为了不破坏资源,我小心保存了原来分配的地址,在所有操作中都使用了Cache-Aligned。
我相信这个问题有更好的解决方案,我一直保持这个问题。
【问题讨论】:
【参考方案1】:您的解决方案是正确的。无法刷新缓存行的子集。
通常这种行为对程序是透明的,但在多线程代码中以及与硬件加速器共享内存时会变得可见。
【讨论】:
以上是关于使数据缓存的特定区域无效而不刷新其内容的主要内容,如果未能解决你的问题,请参考以下文章
如何使控制器中的缓存数据 [OutputCache] 无效?
如何使用 Retrofit 和 OKHttp 在下一次请求时使缓存路由无效/强制更新?