Cortex-M3 的 CMSIS 库中的数据内存屏障 (DMB)
在 gcc 的 CMSIS 定义中,您可以找到如下内容:
static __INLINE void __DMB(void) { __ASM volatile ("dmb"); }
我的问题是:如果内存屏障不在 clobber 列表中声明“内存”,那么它有什么用处?
这是 core_cm3.h 中的错误还是 gcc 在没有任何额外帮助的情况下应该正确运行的原因?
In the CMSIS definitions for gcc you can find something like this:
static __INLINE void __DMB(void) { __ASM volatile ("dmb"); }
My question is: what use does a memory barrier have if it does not declare "memory" in the clobber list?
Is it an error in the core_cm3.h or is there a reason why gcc should behave correctly without any additional help?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我用 gcc 4.5.2(用 LTO 构建)做了一些测试。如果我编译此代码:
使用arm-none-eabi-gcc -Os -mcpu=cortex-m3 -mthumb -c dmb.c,然后从arm-none-eabi-objdump - d dmb.o 我明白了:
很明显
__DBM()
仅插入dmb
指令,并且需要DMB2()
实际上强制编译器刷新寄存器中缓存的值。我想我发现了 CMSIS 错误。
I did some testing with gcc 4.5.2 (built with LTO). If I compile this code:
using
arm-none-eabi-gcc -Os -mcpu=cortex-m3 -mthumb -c dmb.c
, then fromarm-none-eabi-objdump -d dmb.o
I get this:It is obvious that
__DBM()
only inserts thedmb
instruction and it takesDMB2()
to actually force the compiler to flush the values cached in the registers.I guess I found a CMSIS bug.
恕我直言,CMSIS 版本是正确的。
在没有 clobber 列表中内存的情况下注入屏障指令完全实现了它应该做的事情:
如果先前对“x”变量的写入被缓冲,那么它就会被提交。这是有用的,例如,如果您要将“x”地址作为 DMA 地址传递,或者如果您要设置 MPU。
它对返回“x”没有影响(即使你省略了内存屏障,你的程序也保证是正确的)。
另一方面,通过在 clobber 列表中插入内存,在前面的示例(DMA、MPU ..)等情况下不会产生任何影响。
后一种情况的唯一区别是,如果您有一个 ISR 在“strb”之后修改“x”的值,那么将返回的值是 ISR 修改的值,因为破坏导致编译器从内存中读取并再次注册。
但如果你想获得这个东西,那么你应该使用“易失性”变量。
换句话说:屏障强制缓存与内存提交,以保证与可能访问 RAM 内存的其他硬件资源的一致性,而破坏内存会导致编译器停止假设内存未更改并在本地寄存器中再次读取,这是另一个问题具有不同目的的东西(内存更改是否仍在缓存中或已提交到 RAM 上并不重要,因为最终的 asm 加载操作保证在这两种情况下都可以无障碍地工作)。
IMHO the CMSIS version is right.
Injecting the barrier instruction without the memory in clobber list achieves exactly what it is supposed to do:
If the previous write on "x" variable was buffered then it is committed. This is useful, for example, if you are going to pass "x" address to as a DMA address, or if you are going to setup MPU.
It has no effect on returning "x" (your program is guaranteed to be correct even if you omit memory barrier).
On the other hand by inserting memory in clobber list, you have no kind of effect in situations like the example before (DMA, MPU..).
The only difference in the latter case is that if you have for example an ISR modifying the value of "x" right after "strb" , then the value that will be returned is the value modified by the ISR, because the clobber caused the compiler to read from memory to register again.
But if you want to obtain this thing then you should use "volatile" variables.
In other words: the barrier forces cache vs memory commit in order to guarantee consistency with other HW resources that might access RAM memory, while clobbering memory causes compiler to stop assuming the memory has not changed and to read again in local registers, that is another thing with different purposes (it does not matter if a memory change is still in cache or already committed on RAM, because an eventual asm load operation is guaranteed to work in both cases without barriers).