ARM Cortex M3 上的原子 int64_t

Atomic int64_t on ARM Cortex M3

由于我的编译器仍然不支持 c++11 和 std::atomic,我不得不通过 ldrex-strex 对手动实现它。

我的问题是:使用 ldrex 和 strex 'atomically' 读取-修改-写入 int64_t 的正确方法是什么?

像这样的简单解决方案似乎行不通(一直是 STREXW returns 1 之一):

volatile int64_t value;
int64_t temp;

do
{
    int32_t low = __LDREXW( (uint32_t *)&value );
    int32_t high = __LDREXW( ((uint32_t *)&value)+1 );

    temp = (int64_t)low | ( (int64_t)high<<32);
    temp++;    

} while( __STREXW( temp, (uint32_t *)&value) |  __STREXW( temp>>32, ((uint32_t *)&value)+1) );

我在手册中找不到任何关于指向不同地址的几个顺序 LDREX 或 STREX 指令的任何信息,但在我看来应该允许这样做。

否则在某些情况下,多个线程将无法更改两个不同的原子变量。

这永远行不通,因为您不能那样嵌套独占。在实现方面,Cortex-M3 本地独占监视器甚至不跟踪地址 - the exclusive reservation granule is the entire address space - 因此单独跟踪每个单词的假设已经无效。但是,您甚至不需要考虑任何实现细节,因为架构已经明确排除了背靠背 strex:

If two STREX instructions are executed without an intervening LDREX the second STREX returns a status value of 1. This means that:

  • Every STREX must have a preceding LDREX associated with it in a given thread of execution.
  • It is not necessary for every LDREX to have a subsequent STREX .

由于 Cortex-M3(和一般的 ARMv7-M)没有像 ARMv7-A 那样的 ldrexd,您必须使用单独的锁来控制对变量的所有访问,或者只需禁用读取-修改-写入周围的中断。如果可能的话,首先重新设计不需要原子 64 位类型的东西真的会更好,因为你仍然只能实现相对于同一核心上的其他线程的原子性 - 你根本无法制作任何 64 - 从外部代理(如 DMA 控制器)的角度来看,位操作是原子的。

我只是看看 gcc 是如何做的,并使用相同的指令序列。

gcc 4.8.2 声称 实现 std::atomic<int64_t> 并且 is_lock_free() 返回 true,即使 -mcpu=cortex-m3 也是如此。 不幸的是,它并没有真正起作用。它生成的代码不 link 或不工作,因为没有实现它试图使用的辅助函数 。 (感谢@Notlikethat 的尝试。)

Here's the test code I tried。如果 link 已死,请查看此答案的旧版本。我将留下这个答案,以防这个想法对 gcc 确实 制作有用代码的相关案例中的任何人有用。