Cortex-M3 的 CMSIS 库中的数据内存屏障 (DMB)

发布于 2024-11-25 05:48:54 字数 230 浏览 5 评论 0原文

在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

何以心动 2024-12-02 05:48:54

我用 gcc 4.5.2(用 LTO 构建)做了一些测试。如果我编译此代码:

static inline void __DMB(void) { asm volatile ("dmb"); }
static inline void __DMB2(void) { asm volatile ("dmb" ::: "memory"); }

char x;

char test1 (void)
{
  x = 15;
  return x;
}

char test2 (void)
{
  x = 15;
  __DMB();
  return x;
}

char test3 (void)
{
  x = 15;
  __DMB2();
  return x;
}

使用arm-none-eabi-gcc -Os -mcpu=cortex-m3 -mthumb -c dmb.c,然后从arm-none-eabi-objdump - d dmb.o 我明白了:

00000000 <test1>:
   0:   4b01        ldr r3, [pc, #4]    ; (8 <test1+0x8>)
   2:   200f        movs    r0, #15
   4:   7018        strb    r0, [r3, #0]
   6:   4770        bx  lr
   8:   00000000    .word   0x00000000

0000000c <test2>:
   c:   4b02        ldr r3, [pc, #8]    ; (18 <test2+0xc>)
   e:   200f        movs    r0, #15
  10:   7018        strb    r0, [r3, #0]
  12:   f3bf 8f5f   dmb sy
  16:   4770        bx  lr
  18:   00000000    .word   0x00000000

0000001c <test3>:
  1c:   4b03        ldr r3, [pc, #12]   ; (2c <test3+0x10>)
  1e:   220f        movs    r2, #15
  20:   701a        strb    r2, [r3, #0]
  22:   f3bf 8f5f   dmb sy
  26:   7818        ldrb    r0, [r3, #0]
  28:   4770        bx  lr
  2a:   bf00        nop
  2c:   00000000    .word   0x00000000

很明显 __DBM() 仅插入 dmb 指令,并且需要DMB2() 实际上强制编译器刷新寄存器中缓存的值。

我想我发现了 CMSIS 错误。

I did some testing with gcc 4.5.2 (built with LTO). If I compile this code:

static inline void __DMB(void) { asm volatile ("dmb"); }
static inline void __DMB2(void) { asm volatile ("dmb" ::: "memory"); }

char x;

char test1 (void)
{
  x = 15;
  return x;
}

char test2 (void)
{
  x = 15;
  __DMB();
  return x;
}

char test3 (void)
{
  x = 15;
  __DMB2();
  return x;
}

using arm-none-eabi-gcc -Os -mcpu=cortex-m3 -mthumb -c dmb.c, then from arm-none-eabi-objdump -d dmb.o I get this:

00000000 <test1>:
   0:   4b01        ldr r3, [pc, #4]    ; (8 <test1+0x8>)
   2:   200f        movs    r0, #15
   4:   7018        strb    r0, [r3, #0]
   6:   4770        bx  lr
   8:   00000000    .word   0x00000000

0000000c <test2>:
   c:   4b02        ldr r3, [pc, #8]    ; (18 <test2+0xc>)
   e:   200f        movs    r0, #15
  10:   7018        strb    r0, [r3, #0]
  12:   f3bf 8f5f   dmb sy
  16:   4770        bx  lr
  18:   00000000    .word   0x00000000

0000001c <test3>:
  1c:   4b03        ldr r3, [pc, #12]   ; (2c <test3+0x10>)
  1e:   220f        movs    r2, #15
  20:   701a        strb    r2, [r3, #0]
  22:   f3bf 8f5f   dmb sy
  26:   7818        ldrb    r0, [r3, #0]
  28:   4770        bx  lr
  2a:   bf00        nop
  2c:   00000000    .word   0x00000000

It is obvious that __DBM() only inserts the dmb instruction and it takes DMB2() to actually force the compiler to flush the values cached in the registers.

I guess I found a CMSIS bug.

爱的故事 2024-12-02 05:48:54

恕我直言,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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文