为啥 DSB 不刷新缓存?
Posted
技术标签:
【中文标题】为啥 DSB 不刷新缓存?【英文标题】:Why does DSB not flush the cache?为什么 DSB 不刷新缓存? 【发布时间】:2021-12-09 01:58:59 【问题描述】:我正在使用 LWIP 和 HAL 驱动程序在 STM32H725VG 上调试 HTTP 服务器,这些驱动程序最初都是由 STM32CubeMX 生成的。问题在于,在某些情况下,通过HAL_ETH_Transmit
发送的数据有一些八位字节被 0x00 替换,而这些损坏的内容成功地到达了客户端。
我检查了缓冲区中作为参数传递给HAL_ETH_Transmit
的数据在调用此函数之前和之后都完好无损。因此,显然,从 RAM 传输到 MAC 时会发生损坏,因为校验和是根据损坏的数据计算的。所以我认为问题可能是由于缓存和DMA之间的交互。我试过禁用 D-cache,然后没有发生损坏。
然后我想我应该只使用 __DSB()
指令将缓存的数据写入 RAM。启用 D-cache 后,我在调用 HAL_ETH_Transmit
之前添加了 __DSB()
(在 STM32CubeMX 生成的 low_level_output
函数中),然后……什么也没发生:数据仍然损坏。
然后,经过一些实验,我发现SCB_CleanDCache()
在__DSB()
之后(或代替)调用可以解决问题。
这让我很奇怪。 DSB
指令的描述为as follows:
数据同步屏障作为一种特殊的内存屏障。该指令执行后,程序顺序中没有指令,直到该指令完成。该指令在以下情况下完成:
此指令完成之前的所有显式内存访问。 此指令完成之前的所有缓存、分支预测器和 TLB 维护操作。
而description of SCB_DisableDCache
有以下关于SCB_CleanDCache
的注释:
禁用数据缓存时,您必须清理 (
SCB_CleanDCache
) 整个缓存,以确保将所有脏数据刷新到外部内存。
为什么DSB
在“所有显式内存访问”完成时不刷新缓存,这似乎包括刷新缓存?
【问题讨论】:
【参考方案1】:dsb ish
用作线程间内存顺序的内存屏障;它只是命令当前 CPU 访问相干缓存。您不会期望dsb ish
刷新任何缓存,因为在同一内部可共享缓存一致性域中的可见性不需要这样做。就像您引用的手册中所说的那样,它完成了内存操作。
对回写缓存的可缓存内存操作只更新缓存;等待它们完成并不意味着刷新缓存。
我认为您的 ARM 系统对于微控制器与 DSP 有多个一致性域?您的 __DSB
内在函数是否编译为 dsb sy
指令?假设不刷新缓存,他们的意思大概是它命令内存/缓存操作,包括显式刷新,这仍然是必要的。
【讨论】:
是的,它编译为dsb sy
(宏的定义拼写为dsb 0xF
)。
@Ruslan:好的,我认为这只是意味着确保之前的任何内存操作都已完成,但不会追溯使其不可缓存。请注意,某些编译器(如 GCC)以前使用 dmb sy
而不是 ish
用于 std::atomic
代码,因此如果运行以这种方式编译的代码恰好使用了一些原子引用,则使 dmb sy
刷新缓存将是性能灾难计数或即使在没有启动多个线程的二进制文件中也可能发生的事情。【参考方案2】:
我会把钱花在性能上。
刷新缓存意味着将数据从缓存写入内存。内存访问很慢。
L1 缓存大小(假设 ARM Cortex-A9)为 32KB。您不想无缘无故地将整个 32KB 从缓存移动到内存中。可能有 L2 缓存,很容易达到 512KB-1MB(可能更多)。你也真的不想移动整个 L2。
事实上,您的整个 DMA 传输可能小于缓存的大小。根本没有理由这样做。
【讨论】:
以上是关于为啥 DSB 不刷新缓存?的主要内容,如果未能解决你的问题,请参考以下文章