我正在使用Xilinx Zedboard开发嵌入式系统项目。该板能够不对称地拆分它的双核ARM A9处理器,同时运行两个独立的程序。我已经将电路板配置为在一个核心上运行Linux,在另一个核心上运行裸机应用程序作为硬件控制器。对于处理器间通信,我正在利用两个处理器之间共享的片上存储器。我正在努力解决我的锁定实施问题,如果有人遇到过这类问题或者能够指出我正确的方向,我很好奇。
我在ARM参考网站http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html上找到了一个互斥实现,我已经将它改编为C内联汇编。测试后,锁定功能似乎挂了,我不明白为什么。我在装配方面的经验有点受限,我在学校里看到它并理解高级概念,但我在低级实现中完全迷失了。
这看起来是正确的还是这是正确的方法?我只需要一个简单的机制,允许我对进程间通信结构(几个消息队列)执行原子操作。
mutex.h
#ifndef __OCM_MUTEX_H__
#define __OCM_MUTEX_H__
#include <stdint.h>
#define LOCKED 1
#define UNLOCKED 0
typedef uint32_t mutex_t;
extern void ocm_lock_mutex(volatile mutex_t* mutex);
extern void ocm_unlock_mutex(volatile mutex_t* mutex);
#endif
mutex.c
#include "mutex.h"
void ocm_lock_mutex(volatile mutex_t* mutex) {
int result;
const uint32_t locked = LOCKED;
__asm__ __volatile__("@ocm_lock_mutex\n"
"1: LDREX %[r2], [%[r0]]\n"
" CMP %[r2], %[locked]\n"
" BEQ 2f\n"
" STREXNE %[r2], %[locked], [%[r0]]\n"
" CMP %[r2], #1\n"
" BEQ 1b\n"
" DMB\n"
" B 3f\n"
"2: WFE\n"
" B 1b\n"
"3: NOP\n"
: [r2] "=r" (result), [r0] "=r" (mutex)
: [locked] "r" (locked));
}
void ocm_unlock_mutex(volatile mutex_t* mutex) {
const uint32_t unlocked = UNLOCKED;
__asm__ __volatile__("@ocm_unlock_mutex\n"
" DMB\n"
" STR %[unlocked], [%[r0]]\n"
" DSB\n"
" SEV\n"
: [r0] "=r" (mutex)
: [unlocked] "r" (unlocked));
}
答案 0 :(得分:2)
为什么不使用GNU程序集?它应该看起来像这样
.equ locked,1
.equ unlocked,0
@ lock_mutex
@ Declare for use from C as extern void lock_mutex(void * mutex);
.global lock_mutex
lock_mutex:
LDR r1, =locked
1: LDREX r2, [r0]
CMP r2, r1 @ Test if mutex is locked or unlocked
BEQ 2f @ If locked - wait for it to be released, from 2
STREXNE r2, r1, [r0] @ Not locked, attempt to lock it
CMPNE r2, #1 @ Check if Store-Exclusive failed
BEQ 1b @ Failed - retry from 1
# Lock acquired
DMB @ Required before accessing protected resource
BX lr
2: @ Take appropriate action while waiting for mutex to become unlocked
@ WAIT_FOR_UPDATE
B 1b @ Retry from 1
@ unlock_mutex
@ Declare for use from C as extern void unlock_mutex(void * mutex);
.global unlock_mutex
unlock_mutex:
LDR r1, =unlocked
DMB @ Required before releasing protected resource
STR r1, [r0] @ Unlock mutex
@ SIGNAL_UPDATE
BX lr
然后它的转储看起来像这样
$ arm-linux-gnueabihf-objdump -d mutex.o
mutex.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <lock_mutex>:
0: e3a01001 mov r1, #1
4: e1902f9f ldrex r2, [r0]
8: e1520001 cmp r2, r1
c: 0a000004 beq 24 <lock_mutex+0x24>
10: 11802f91 strexne r2, r1, [r0]
14: 13520001 cmpne r2, #1
18: 0afffff9 beq 4 <lock_mutex+0x4>
1c: f57ff05f dmb sy
20: e12fff1e bx lr
24: eafffff6 b 4 <lock_mutex+0x4>
00000028 <unlock_mutex>:
28: e3a01000 mov r1, #0
2c: f57ff05f dmb sy
30: e5801000 str r1, [r0]
34: e12fff1e bx lr
然而,我想知道你是否确实设法将两个核心配置为包含在核心一致性中。据我所知,您可以指定哪些内核参与ldrex / strex操作。
答案 1 :(得分:0)
至于你的代码挂起的原因,这可能是因为WFE指令。 如果没有事件发生,它将什么也不做。永远。 如果启用并生成了事件,请先检查。
(另外,请检查ARM体系结构参考手册中对STREX和LDREX的使用限制,这应该在A2.9.4节“使用限制”中)
有关如何实现自旋锁的示例: https://www.doulos.com/knowhow/arm/Hints_and_Tips/Implementing_Semaphores/
将他们的示例应用于您的代码将导致类似这样的事情:
__asm__ __volatile__("@ocm_lock_mutex\n"
" LDREX %[r2], [%[r0]]\n"
" CMP %[r2], %[locked]\n"
" STREXNE %[r2], %[locked], [%[r0]]\n"
" CMPNE %[r2], #1\n"
" BEQ ocm_lock_mutex\n"
: [r2] "=r" (result), [r0] "=r" (mutex)
: [locked] "r" (locked));
这将实现忙碌等待的互斥锁。
如果您希望代码告诉您是否在没有繁忙等待的情况下获取互斥锁,只需修改结束:
__asm__ __volatile__("@ocm_lock_mutex\n"
[...]
" CMPNE %[r2], #1\n"
" BEQ ocm_lock_mutex_end\n"
" MOV %[r2], #2\n"
"@ocm_lock_mutex_end\n"
" NOP\n"
: [r2] "=r" (result), [r0] "=r" (mutex)
: [locked] "r" (locked));
然后检查C:
if (result==0) {/*You didn't get the mutex, it was locked*/}
else if (result==1) {/*You didn't get the mutex, access not exclusive*/}
else if (result==2) {/*You got the mutex!*/}
(如ARM体系结构参考手册,2005版,A2.9.4“加载和存储操作”所示)
在C中构建外部“忙等待”循环是完全合理的。 或者,如果您想要一个基于中断的方案,请从那里暂停操作。
经验法则:
保持内联汇编代码尽可能小且无循环。
让你的内联汇编一次只做一件事。