【问题标题】:Cortex M3, STM32, thumb2: My inc and dec operations are not atomic, but should be. What's wrong here?Cortex M3、STM32、thumb2:我的 inc 和 dec 操作不是原子的,但应该是原子的。这里有什么问题?
【发布时间】:2021-12-10 05:44:24
【问题描述】:

我需要一个线程保存 idx++ 和 idx-- 操作。 禁用中断,即使用临界区,是一回事,但我想要 了解为什么我的操作不是原子的,正如我所期望的那样? 这是使用 segger 臭氧显示的带有内联汇编代码的 C 代码: (另外请注意,变量的地址显示32位变量在内存中是32位对齐的,8位和16位变量都是16位对齐的)

volatile static U8 dbgIdx8 = 1000U;
volatile static U16 dbgIdx16 = 1000U;
volatile static U32 dbgIdx32 = 1000U;
      dbgIdx8 ++;
     080058BE   LDR            R3, [PC, #48]                 
     080058C0   LDRB           R3, [R3]
     080058C2   UXTB           R3, R3
     080058C4   ADDS           R3, #1
     080058C6   UXTB           R2, R3
     080058C8   LDR            R3, [PC, #36]                 
     080058CA   STRB           R2, [R3]

      dbgIdx16 ++;
     080058CC   LDR            R3, [PC, #36]                 
     080058CE   LDRH           R3, [R3]
     080058D0   UXTH           R3, R3
     080058D2   ADDS           R3, #1
     080058D4   UXTH           R2, R3
     080058D6   LDR            R3, [PC, #28]                 
     080058D8   STRH           R2, [R3]

      dbgIdx32 ++;
     080058DA   LDR            R3, [PC, #28]                 
     080058DC   LDR            R3, [R3]
     080058DE   ADDS           R3, #1
     080058E0   LDR            R2, [PC, #20]                 
     080058E2   STR            R3, [R2]

【问题讨论】:

  • 正常的 C 增量不是原子的,也不是易失的访问。您应该使用来自<stdatomic.h> 的显式原子操作
  • 我的意思是,看看汇编代码。您将 dbgIdx8 加载到寄存器中,增加该寄存器,然后稍后将其存储回几条指令。这无论如何都不是原子的,加载和存储之间可能会发生各种外部事物。您应该使用独占加载和存储,ldrexb/strexb,并且如前所述,<stdatomic.h> 操作将正确发出这些指令。

标签: c stm32 atomic microcontroller cortex-m3


【解决方案1】:

不保证++-- 是原子的。如果您需要保证原子性,则必须找到其他方法。

正如@StaceyGirl 在评论中指出的那样,您也许可以使用<stdatomic.h> 的设施。例如,我看到定义了一个原子atomic_fetch_add 函数,它的作用类似于您正在努力争取的后缀++。还有一个atomic_fetch_sub

或者,您可能有一些编译器内在函数可供您以某种特定于处理器的方式执行原子增量。

【讨论】:

  • 有关 Cortex-M 上特定于处理器的原子,请参阅 this answer(我的)。
【解决方案2】:
  1. ARM 皮质内核不会修改内存。所有内存修改都作为 RMW(读-修改-写)操作执行,默认情况下不是原子操作。

但是 Cortex M3 有特殊指令来锁定对内存位置的访问。 LDREX 和 STREX。 https://developer.arm.com/documentation/100235/0004/the-cortex-m33-instruction-set/memory-access-instructions/ldaex-and-stlex

您可以直接在 C 代码中使用它们,而无需通过内部函数接触程序集。

  1. 不要在任何对性能(您希望锁定尽可能短的时间)敏感的程序中使用非 32 位数据类型。大多数较短的数据类型操作都会添加一些额外的代码。

【讨论】:

  • Cortex M3 也有 LDREX & STREX 指令。 g++ 在 C++ 中使用 std::atomic 时会发出它们。我猜现代 C 编译器也是如此。不幸的是,M0 内核缺少这些指令,并且在 C++ 代码中使用原子会导致链接错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-23
  • 1970-01-01
  • 2013-07-19
  • 1970-01-01
  • 1970-01-01
  • 2014-09-29
相关资源
最近更新 更多