ARM程序集内联C互斥实现

时间:2013-11-21 03:31:16

标签: c assembly arm mutex xilinx

我正在使用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));
}

2 个答案:

答案 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中构建外部“忙等待”循环是完全合理的。 或者,如果您想要一个基于中断的方案,请从那里暂停操作。

经验法则:

  • 保持内联汇编代码尽可能小且无循环。

  • 让你的内联汇编一次只做一件事。