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 确实 制作有用代码的相关案例中的任何人有用。
由于我的编译器仍然不支持 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 确实 制作有用代码的相关案例中的任何人有用。