Assembly 在Cortex-M0中模拟LDREX/STREX(加载/存储专用)
在Cortex-M3指令集中,存在一系列LDREX/STREX指令,因此,如果使用LDREX指令读取某个位置,则只有在已知该地址未被触及的情况下,后续STREX指令才能写入该地址。通常,其效果是,如果自LDREX以来没有发生中断(“ARM术语中的异常”),STREX将成功,否则将失败 在大脑皮层M0中模拟这种行为最实际的方法是什么?我想为M3编写C代码,并将其移植到M0。在M3上,可以这样说: __inline void do_inc(unsigned int *dat) { while(__strex(__ldrex(dat)+1,dat)) {} } __内联void do_inc(未签名整数*dat) { 而(uu-strex(u-ldrex(dat)+1,dat)){} } 执行原子增量。我能想到的在Cortex-M0上实现类似功能的唯一方法是:Assembly 在Cortex-M0中模拟LDREX/STREX(加载/存储专用),assembly,arm,atomic,cortex-m,Assembly,Arm,Atomic,Cortex M,在Cortex-M3指令集中,存在一系列LDREX/STREX指令,因此,如果使用LDREX指令读取某个位置,则只有在已知该地址未被触及的情况下,后续STREX指令才能写入该地址。通常,其效果是,如果自LDREX以来没有发生中断(“ARM术语中的异常”),STREX将成功,否则将失败 在大脑皮层M0中模拟这种行为最实际的方法是什么?我想为M3编写C代码,并将其移植到M0。在M3上,可以这样说: __inline void do_inc(unsigned int *dat) { while(_
SWP
剩余,但它是一个功能较弱的原子指令
尽管如此,中断禁用仍能正常工作。:-)
编辑:
没有SWP在-m0上,对不起,超级猫
好吧,看来你只剩下中断禁用了。
您可以使用gcc可编译内联asm作为如何禁用和正确恢复它的指南:
STREX/LDREX用于多核处理器访问跨核共享的内存中的共享项。ARM在记录这些方面做得非常糟糕,您必须阅读amba/axi和ARM以及trm文档中的行之间的内容才能弄清楚这一点 它的工作原理是,如果您有一个支持STREX/LDREX的内核,如果您有一个支持独占访问的内存控制器,那么如果内存控制器看到一对独占操作,其间没有其他内核访问该内存,那么您将返回EX_ok,而不是ok。arm文档告诉芯片设计人员,如果它是单处理器(不实现多核功能),那么您不必支持exokay just return OK,从软件角度来看,这会打破LDREX/STREX对访问逻辑的影响(软件在无限循环中旋转,因为它永远不会返回成功),一级缓存确实支持它,所以感觉它可以工作 对于单处理器和不访问跨内核共享内存的情况,请使用SWP m0既不支持ldrex/strex,也不支持swp,但它们基本上给了你什么?他们只是给你一个访问权限,而不受你访问权限的影响。为了防止你踩自己的脚,只需在这段时间内禁用中断,就像我们自黑暗时代以来进行原子访问的方式一样。如果你想保护自己,如果你的外设可能会干扰,那么就没有办法绕过它,甚至交换也可能没有帮助
因此,只需禁用关键部分周围的中断。Cortex-M3设计用于重低延迟和低抖动的多任务处理,即它的中断控制器与内核协作,以保证从中断触发到中断处理的周期数。ldrex/strex的实现是为了配合所有这些(我指的是中断屏蔽和其他细节,例如通过位带别名设置原子位),否则单核、非MMU、非缓存µC几乎没有用处。如果它没有实现它,低优先级任务将不得不持有一个锁,tha可能会产生小的优先级反转,产生延迟和抖动,这是硬实时系统(它的设计,虽然概念过于宽泛)无法应付的,至少不在“重试”允许的数量级内失败的ldrex/strex具有的语义 另一方面,严格说来,Cortex-M0的计时和抖动更为传统(即,当中断到达时,它不会中止内核上的指令),会受到更大的抖动和延迟。在这个问题上(同样,严格地说是计时),它与旧型号(即arm7tdmi)更具可比性,后者也缺乏原子加载/修改/存储以及原子增量和减量以及其他低延迟协作指令,需要更频繁地禁用/启用中断 我在Cortex-M3中使用了如下内容:
#define unlikely(x) __builtin_expect((long)(x),0)
static inline int atomic_LL(volatile void *addr) {
int dest;
__asm__ __volatile__("ldrex %0, [%1]" : "=r" (dest) : "r" (addr));
return dest;
}
static inline int atomic_SC(volatile void *addr, int32_t value) {
int dest;
__asm__ __volatile__("strex %0, %2, [%1]" :
"=&r" (dest) : "r" (addr), "r" (value) : "memory");
return dest;
}
/**
* atomic Compare And Swap
* @param addr Address
* @param expected Expected value in *addr
* @param store Value to be stored, if (*addr == expected).
* @return 0 ok, 1 failure.
*/
static inline int atomic_CAS(volatile void *addr, int32_t expected,
int32_t store) {
int ret;
do {
if (unlikely(atomic_LL(addr) != expected))
return 1;
} while (unlikely((ret = atomic_SC(addr, store))));
return ret;
}
换句话说,它将ldrex/strex引入众所周知的链接加载和存储条件,并使用它实现比较和交换语义
如果您的代码只在比较和交换方面做得很好,您可以在cortex-m0上实现它,如下所示:
static inline int atomic_CAS(volatile void *addr, int32_t expected,
int32_t store) {
int ret = 1;
__interrupt_disable();
if (*(volatile uint32_t *)addr) == expected) {
*addr = store;
ret = 0;
}
__interrupt_enable();
return ret;
}
那是最常用的