ARM体系架构下的同步操作

发布者:Qinghua2022最新更新时间:2016-06-23 来源: eefocus关键字:ARM  体系架构  同步操作 手机看文章 扫描二维码
随时随地手机看文章
处理器在访问共享资源时,必须对临界区进行同步,即保证同一时间内,只有一个对临界区的访问者。

当共享资源为一内存地址时,原子操作是对该类型共享资源同步访问的最佳方式。

随着应用的日益复杂和SMP的广泛使用,处理器都开始提供硬件同步原语以支持原子地更新内存地址。

CISC处理器比如IA32,可以提供单独的多种原子指令完成复杂的原子操作,由处理器保证读-修改-写回过程的原子性。

而RISC则不同,由于除Load和Store的所有操作都必须在寄存器中完成,

如何保证从装载内存地址到寄存器,到修改寄存器中的值,再到将寄存器中的值写回内存中可以原子性的完成,便成为了处理器设计的关键。

从ARMv6架构开始,ARM处理器提供了Exclusive accesses同步原语,包含两条指令:

LDREX
STREX

 

LDREX和STREX指令,将对一个内存地址的原子操作拆分成两个步骤,

同处理器内置的记录exclusive accesses的exclusive monitors一起,完成对内存的原子操作。

LDREX

LDREX与LDR指令类似,完成将内存中的数据加载进寄存器的操作。

与LDR指令不同的是,该指令也会同时初始化exclusive monitor来记录对该地址的同步访问。例如

LDREX R1, [R0]

会将R0寄存器中内存地址的数据,加载进R1中并更新exclusive monitor。

STREX

该指令的格式为:

STREX Rd, Rm, [Rn]

 

STREX会根据exclusive monitor的指示决定是否将寄存器中的值写回内存中。

如果exclusive monitor许可这次写入,则STREX会将寄存器Rm的值写回Rn所存储的内存地址中,并将Rd寄存器设置为0表示操作成功。

如果exclusive monitor禁止这次写入,则STREX指令会将Rd寄存器的值设置为1表示操作失败并放弃这次写入。

应用程序可以根据Rd中的值来判断写回是否成功。

在这篇文章里,首先会以Linux Kernel中ARM架构的原子相加操作为例,介绍这两条指令的使用方法;

之后,会介绍GCC提供的一些内置函数,这些同步函数使用这两条指令完成同步操作。

Linux Kernel中的atomic_add函数

如下是Linux Kernel中使用的atomic_add函数的定义,它实现原子的给 v 指向的atomic_t增加 i 的功能。

 1 static inline void atomic_add(int i, atomic_t *v)
 2 {
 3         unsigned long tmp;
 4         int result;
 5 
 6         __asm__ __volatile__("@ atomic_add\n"
 7 "1:     ldrex   %0, [%3]\n"
 8 "       add     %0, %0, %4\n"
 9 "       strex   %1, %0, [%3]\n"
10 "       teq     %1, #0\n"
11 "       bne     1b"
12         : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
13         : "r" (&v->counter), "Ir" (i)
14         : "cc");
15 }

 

在第7行,使用LDREX指令将v->counter所指向的内存地址的值装入寄存器中,并初始化exclusive monitor。

在第8行,将该寄存器中的值与i相加。

在第9,10,11行,使用STREX指令尝试将修改后的值存入原来的地址,

如果STREX写入%1寄存器的值为0,则认为原子更新成功,函数返回;

如果%1寄存器的值不为0,则认为exclusive monitor拒绝了本次对内存地址的访问,

则跳转回第7行重新进行以上所述的过程,直到成功将修改后的值写入内存为止。

该过程可能多次反复进行,但可以保证,在最后一次的读-修改-写回的过程中,没有其他代码访问该内存地址。

static inline void atomic_set(atomic_t *v, int i)
{
    unsigned long tmp;

    __asm__ __volatile__("@ atomic_set/n"
"1:    ldrex    %0, [%1]/n"
"    strex    %0, %2, [%1]/n"
"    teq    %0, #0/n"
"    bne    1b"
    : "=&r" (tmp)
    : "r" (&v->counter), "r" (i)
    : "cc");
}

输入为v(原子变量),i(要设置的值),均存放在动态分配的寄存器中。tmp用来指示操作是否成功。

GCC内置的原子操作函数

看了上面的GCC内联汇编,是不是有点晕?

在用户态下,GCC为我们提供了一系列内置函数,这些函数可以让我们既享受原子操作的好处,

又免于编写复杂的内联汇编指令。这一系列的函数均以__sync开头,分为如下几类:

type __sync_fetch_and_add (type *ptr, type value, ...)
type __sync_fetch_and_sub (type *ptr, type value, ...)
type __sync_fetch_and_or (type *ptr, type value, ...)
type __sync_fetch_and_and (type *ptr, type value, ...)
type __sync_fetch_and_xor (type *ptr, type value, ...)
type __sync_fetch_and_nand (type *ptr, type value, ...)

 

这一系列函数完成对ptr所指向的内存地址的对应操作,并返回操作之前的值。

type __sync_add_and_fetch (type *ptr, type value, ...)
type __sync_sub_and_fetch (type *ptr, type value, ...)
type __sync_or_and_fetch (type *ptr, type value, ...)
type __sync_and_and_fetch (type *ptr, type value, ...)
type __sync_xor_and_fetch (type *ptr, type value, ...)
type __sync_nand_and_fetch (type *ptr, type value, ...)

这一系列函数完成对ptr所指向的内存地址的对应操作,并返回操作之后的值。

bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)
type __sync_val_compare_and_swap (type *ptr, type oldval, type newval, ...)

这两个函数完成对变量的原子比较和交换。

即如果ptr所指向的内存地址存放的值与oldval相同的话,则将其用newval的值替换。

返回bool类型的函数返回比较的结果,相同为true,不同为false;

返回type的函数返回的是ptr指向地址交换前存放的值。

LDREX 和 STREX

独占加载和存储寄存器。

语法

LDREX{cond} Rt, [Rn {, #offset}]
STREX{cond} Rd, Rt, [Rn {, #offset}]
LDREXB{cond} Rt, [Rn]
STREXB{cond} Rd, Rt, [Rn]
LDREXH{cond} Rt, [Rn]
STREXH{cond} Rd, Rt, [Rn]
LDREXD{cond} Rt, Rt2, [Rn]
STREXD{cond} Rd, Rt, Rt2, [Rn]

 

其中:

cond

是一个可选的条件代码(请参阅条件执行)。

 
Rd 

是存放返回状态的目标寄存器。

 
Rt

是要加载或存储的寄存器。

 
Rt2

为进行双字加载或存储时要用到的第二个寄存器。

 
Rn

是内存地址所基于的寄存器。

 
offset

为应用于 Rn 中的值的可选偏移量。offset 只可用于 Thumb-2 指令中。 如果省略 offset,则认为偏移量为 0。

LDREX

LDREX 可从内存加载数据。

  • 如果物理地址有共享 TLB 属性,则 LDREX 会将该物理地址标记为由当前处理器独占访问,并且会清除该处理器对其他任何物理地址的任何独占访问标记。

  • 否则,会标记:执行处理器已经标记了一个物理地址,但访问尚未完毕。

STREX

STREX 可在一定条件下向内存存储数据。 条件具体如下:

  • 如果物理地址没有共享 TLB 属性,且执行处理器有一个已标记但尚未访问完毕的物理地址,那么将会进行存储,清除该标记,并在 Rd 中返回值 0。

  • 如果物理地址没有共享 TLB 属性,且执行处理器也没有已标记但尚未访问完毕的物理地址,那么将不会进行存储,而会在 Rd 中返回值 1。

  • 如果物理地址有共享 TLB 属性,且已被标记为由执行处理器独占访问,那么将进行存储,清除该标记,并在 Rd 中返回值 0。

  • 如果物理地址有共享 TLB 属性,但没有标记为由执行处理器独占访问,那么不会进行存储,且会在 Rd中返回值 1。

限制

r15 不可用于 Rd、Rt、Rt2 或 Rn 中的任何一个。

对于 STREX,Rd 一定不能与 Rt、Rt2 或 Rn 为同一寄存器。

对于 ARM 指令:

  • Rt 必须是一个编号为偶数的寄存器,且不能为 r14

  • Rt2 必须为 R(t+1)

  • 不允许使用 offset。

对于 Thumb 指令:

  • r13 不可用于 Rd、Rt 或 Rt2 中的任何一个

  • 对于 LDREXD,Rt 和 Rt2 不可为同一个寄存器

  • offset 的值可为 0-1020 范围内 4 的任何倍数。

用法

利用 LDREX 和 STREX 可在多个处理器和共享内存系统之前实现进程间通信。

出于性能方面的考虑,请将相应 LDREX 指令和 STREX 指令间的指令数控制到最少。

Note

STREX 指令中所用的地址必须要与近期执行次数最多的 LDREX 指令所用的地址相同。 
如果使用不同的地址,则 STREX 指令的执行结果将不可预知。

体系结构

ARM LDREX 和 STREX 可用于 ARMv6 及更高版本中。

ARM LDREXB、LDREXH、LDREXD、STREXB、STREXD 和 STREXH 可用于 ARMv6K 及更高版本中。

所有这些 32 位 Thumb 指令均可用于 ARMv6T2 及更高版本,但 LDREXD 和 STREXD 在 ARMv7-M 架构中不可用。

这些指令均无 16 位版本。

示例

    MOV r1, #0x1                ; load the ‘lock taken’ value
try
    LDREX r0, [LockAddr]        ; load the lock value
    CMP r0, #0                  ; is the lock free?
    STREXEQ r0, r1, [LockAddr]  ; try and claim the lock
    CMPEQ r0, #0                ; did this succeed?
    BNE try                     ; no – try again
    ....                        ; yes – we have the lock

http://lxr.free-electrons.com/source/arch/arm/include/asm/atomic.h?v=2.6.33

/*
*  arch/arm/include/asm/atomic.h
*
*  Copyright (C) 1996 Russell King.
*  Copyright (C) 2002 Deep Blue Solutions Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __ASM_ARM_ATOMIC_H
#define __ASM_ARM_ATOMIC_H

#include 
#include 
#include 

#define ATOMIC_INIT(i)  { (i) }

#ifdef __KERNEL__

/*
* On ARM, ordinary assignment (str instruction) doesn't clear the local
* strex/ldrex monitor on some implementations. The reason we can use it for
* atomic_set() is the clrex or dummy strex done on every exception return.
*/
#define atomic_read(v)  ((v)->counter)
#define atomic_set(v,i) (((v)->counter) = (i))

#if __LINUX_ARM_ARCH__ >= 6

/*
* ARMv6 UP and SMP safe atomic ops.  We use load exclusive and store exclusive to ensure that these are atomic.  
* We may loop to ensure that the update happens.
*/
static inline void atomic_add(int i, atomic_t *v)
{
       unsigned long tmp;
       int result;

       __asm__ __volatile__("@ atomic_add\n"
"1:     ldrex   %0, [%2]\n"
"       add     %0, %0, %3\n"
"       strex   %1, %0, [%2]\n"
"       teq     %1, #0\n"
"       bne     1b"
       : "=&r" (result), "=&r" (tmp)
       : "r" (&v->counter), "Ir" (i)
       : "cc");
}

static inline int atomic_add_return(int i, atomic_t *v)
{
       unsigned long tmp;
       int result;

       smp_mb();

       __asm__ __volatile__("@ atomic_add_return\n"
"1:     ldrex   %0, [%2]\n"
"       add     %0, %0, %3\n"
"       strex   %1, %0, [%2]\n"
"       teq     %1, #0\n"
"       bne     1b"
       : "=&r" (result), "=&r" (tmp)
       : "r" (&v->counter), "Ir" (i)
       : "cc");

       smp_mb();

       return result;
}

static inline void atomic_sub(int i, atomic_t *v)
{
       unsigned long tmp;
       int result;

       __asm__ __volatile__("@ atomic_sub\n"
"1:     ldrex   %0, [%2]\n"
"       sub     %0, %0, %3\n"
"       strex   %1, %0, [%2]\n"
"       teq     %1, #0\n"
"       bne     1b"
       : "=&r" (result), "=&r" (tmp)
       : "r" (&v->counter), "Ir" (i)
       : "cc");
}

static inline int atomic_sub_return(int i, atomic_t *v)
{
       unsigned long tmp;
       int result;

       smp_mb();

       __asm__ __volatile__("@ atomic_sub_return\n"
"1:     ldrex   %0, [%2]\n"
"       sub     %0, %0, %3\n"
"       strex   %1, %0, [%2]\n"
"       teq     %1, #0\n"
"       bne     1b"
       : "=&r" (result), "=&r" (tmp)
       : "r" (&v->counter), "Ir" (i)
       : "cc");

       smp_mb();

       return result;
}

static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
{
       unsigned long oldval, res;

       smp_mb();

       do {
               __asm__ __volatile__("@ atomic_cmpxchg\n"
               "ldrex  %1, [%2]\n"
               "mov    %0, #0\n"
               "teq    %1, %3\n"
               "strexeq %0, %4, [%2]\n"
                   : "=&r" (res), "=&r" (oldval)
                   : "r" (&ptr->counter), "Ir" (old), "r" (new)
                   : "cc");
       } while (res);

       smp_mb();

       return oldval;
}

static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
{
       unsigned long tmp, tmp2;

       __asm__ __volatile__("@ atomic_clear_mask\n"
"1:     ldrex   %0, [%2]\n"
"       bic     %0, %0, %3\n"
"       strex   %1, %0, [%2]\n"
"       teq     %1, #0\n"
"       bne     1b"
       : "=&r" (tmp), "=&r" (tmp2)
       : "r" (addr), "Ir" (mask)
       : "cc");
}

#else /* ARM_ARCH_6 */

#ifdef CONFIG_SMP
#error SMP not supported on pre-ARMv6 CPUs
#endif

static inline int atomic_add_return(int i, atomic_t *v)
{
       unsigned long flags;
       int val;

       raw_local_irq_save(flags);
       val = v->counter;
       v->counter = val += i;
       raw_local_irq_restore(flags);

       return val;
}
#define atomic_add(i, v)        (void) atomic_add_return(i, v)

static inline int atomic_sub_return(int i, atomic_t *v)
{
       unsigned long flags;
       int val;

       raw_local_irq_save(flags);
       val = v->counter;
       v->counter = val -= i;
       raw_local_irq_restore(flags);

       return val;
}
#define atomic_sub(i, v)        (void) atomic_sub_return(i, v)

static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
{
       int ret;
       unsigned long flags;

       raw_local_irq_save(flags);
       ret = v->counter;
       if (likely(ret == old))
               v->counter = new;
       raw_local_irq_restore(flags);

       return ret;
}

static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
{
       unsigned long flags;

       raw_local_irq_save(flags);
       *addr &= ~mask;
       raw_local_irq_restore(flags);
}

#endif /* __LINUX_ARM_ARCH__ */

#define atomic_xchg(v, new) (xchg(&((v)->counter), new))

static inline int atomic_add_unless(atomic_t *v, int a, int u)
{
       int c, old;

       c = atomic_read(v);
       while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)
               c = old;
       return c != u;
}
#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)

#define atomic_inc(v)           atomic_add(1, v)
#define atomic_dec(v)           atomic_sub(1, v)

#define atomic_inc_and_test(v)  (atomic_add_return(1, v) == 0)
#define atomic_dec_and_test(v)  (atomic_sub_return(1, v) == 0)
#define atomic_inc_return(v)    (atomic_add_return(1, v))
#define atomic_dec_return(v)    (atomic_sub_return(1, v))
#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)

#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)

#define smp_mb__before_atomic_dec()     smp_mb()
#define smp_mb__after_atomic_dec()      smp_mb()
#define smp_mb__before_atomic_inc()     smp_mb()
#define smp_mb__after_atomic_inc()      smp_mb()

#include 
#endif
#endif
 

 


关键字:ARM  体系架构  同步操作 引用地址:ARM体系架构下的同步操作

上一篇:x86和arm架构原子操作的区别
下一篇:arm linux 下中断流程简要分析中断处理流程

推荐阅读最新更新时间:2024-03-16 14:58

ARM JTAG仿真器电路讨论
简介:以下是我在实践中的一些积累,发现这点是因为我在尝试用对SAMSUNG S3C44B0 JTAG适用的编程板电路给SAMSUNG的另一款ARM9内核MPU S3C2440 JTAG编程时出现问题,查阅了一些资料后最终解决。希望这些对那些在自制ARM JTAG编程器上遇到困难的朋友一点帮助。 一. JTAG仿真器的实质 JTAG (Joint Test Action Group) 编程调试实质上是利用了MCU/MPU片上自带的跟踪调试功能(需MCU/MPU硬件支持)。JTAG编程板一端与PC的并口相连,另一端连接至目标板,由于通常的MCU/MPU的工作电压在1.8V-3.6V之间,而PC机并口输出的电平逻辑为5V,因此需做电
[单片机]
<font color='red'>ARM</font> JTAG仿真器电路讨论
OpenCV2.0.0移植到ARM9(二)(JZ2440----S3c2440)
1、交叉编译libjpeg 为了使OpenCV能处理jpeg图像,我们必须事先交叉编译好libjpeg这里使用的版本是jpegsrc.v6b。 下载地址:https://sourceforge.net/projects/libjpeg/files/libjpeg/6b/ 这里使用的安装包:jpegsrc.v6b.tar.gz。 (1)解压、配置 jpegsrc.v6b.tar.gz放在/work/system/目录下. 解压:$tar –zvxf jpegsrc.v6b.tar.gz 进入目录:$cd jpeg-6b 在/work/system/目录下新建一个libjpeg-arm目录,命令为:$sudo mkdir
[单片机]
OpenCV2.0.0移植到<font color='red'>ARM</font>9(二)(JZ2440----S3c2440)
基于FPGA与ARM进行遥测数据网络转发的流程剖析
以太网接口通信速度快,传输可靠,使用和配置方便,对于20 Mb/s以下的码速率,100 Mb/s的网卡可以进行不丢包转发,采用TCP包格式还可使设备小型化,便于数据的转发,因此有必要扩展设备的以太网功能。 1 整体模块设计 1.1 系统设计 系统设计框图如图1所示。其中,采编器或接收机解调输出的PCM信号及时钟输入到FPGA中进行帧同步,IRIG—B码信息也送到FPGA中进行解调,得到时间信息。数据与时间一起存入SRAM乒乓缓冲区中,达到一定大小后,FPGA向ARM处理器发器中断,ARM中运行的Linux系统,将数据取走,进行TCP/IP打包,发送给接收计算机。 在设备开始工作前,需要在计算机端进行参数设置,计算机TCP/
[单片机]
基于FPGA与<font color='red'>ARM</font>进行遥测数据网络转发的流程剖析
基于ARM的GPS地面目标跟踪及报警系统的设计
1 引言 随着现代化科技的发展,人们对移动目标监控的要求越来越高。例如,地面目标跟踪及报警系统可协助家长实现对孩子的监控,孩子若遭遇走失或劫持事件,监控中心通过分析手持终端发送的GPS数据确定孩子所在位置,以采取相关安全措施。此外,地面目标跟踪及报警系统在城市出租车调度、物流运输监控等领域都有着广泛的应用前景。 地面目标跟踪及报警系统是伴随着GPS技术和GSM网络的成熟而发展起来的。20世纪90年代初,GPS技术逐渐兴起,基于GPS的移动跟踪、监控系统开始走向市场。近年来,GSM网络发展迅速,其数据传输能力明显加强,并且,GPS技术更加成熟。2000年5月1日,美国政府宣布取消GPS普通定位的选择干扰(SA)政策,这样,一般G
[网络通信]
ARM9处理器与ARM7处理器比较
摘要:ARM处理器是世界上最流行的嵌入式处理器,广泛应用于个人通信等嵌入式领域。ARM7处理器虽然功能强大,但是目前已经开始退出主流应用领域,代替它的是性能更加强大的ARM9系列处理器。介绍了ARM9处理器与ARM7处理器的不同之处,并且给出了ARM9的应用实例。 关键词:ARM9 ARM7 流水线 指令周期 ARM9系列处理器是英国ARM公司设计的主流嵌入式处理器,主要包括ARM9TDMI和ARM9E-S等系列。本文主要介绍它们与ARM7TDMI的结构以及性能比较。 以手机应用为例,2G手机只需提供语音及简单的文字短信功能,而目前的2.5G和未来的3G手机除了提供这两项功能外,还必须提供各种其他的应用功能。主要包括:(1
[嵌入式]
并入软银 9 个月,ARM 发生了哪些改变?
  2016 年 7 月,日本软银集团以 243 亿英镑(约新台币 1.03 兆元)天价收购英国 IP 矽智财大厂  ARM  的消息,震撼全球科技圈。自从 2016 年 9 月软银完成收购至今,9 个多月过去了,软银创办人孙正义眼中“集团未来成长战略的核心之一”──这块烫金拼图  ARM ,发生了哪些变化?又面临哪些新的挑战?下面就随网络通信小编一起来了解一下相关内容吧。   首先, ARM  为了扩大生态圈,更积极走到台前和市场沟通。在刚落幕的 2017 台北国际电脑展中,ARM 以过去罕见的盛大规模参展,IP、 物联网 两大事业群总裁亲自来台发表演说,多位高阶主管轮番召开记者会、解说新产品,相当重视台湾市场。   ARM 
[网络通信]
ARM和台积电合作部署7nm芯片
    ARM和台积电(TSMC)宣布在未来几年深化合作共同创建7nm制程工艺,目前两家公司的重点依然集中在16nm和10nm芯片上。去年夏季的时候,IBM首次宣布创建7nm芯片,尽管这种创新制造工艺由于成本过于昂贵意味着在最近几年无法量产,IBM表示7nm芯片有望在2018年上市,最迟要推迟至2019年。     而英特尔同样加入这场竞赛中,已经退职10nm芯片生产至2017年下半年,这就意味着7nm芯片要在2019年年末和2020年年初的时候上市,这也给IBM和其他企业在前言科技方面超越英特尔的机会。目前尚不清楚台积电何时开始量产7nm芯片,但目前掌握的信息是10nm芯片有望在明年年初上市,7nm则有望在2019年。
[家用电子]
Actel为嵌入ARM7功能的FPGA推出开发工具套装
Actel公司宣布推出CoreMP7开发工具套装,这是完整的软、硬件开发环境,针对该公司CoreMP7软ARM7处理器内核的执行而设。CoreMP7开发工具套装包含屡获殊荣的CoreMP7、具Actel ARM7功能的M7 ProASIC3器件和FPGA开发工具,为用户提供了一切所需工具,可以快速和简便地评估及设计以FPGA为基础的系统级芯片 (SoC) 应用。 Actel IP市务经理Mike Thompson称:“CoreMP7开发工具充分发挥了FPGA的灵活性结合快速推向市场和业界标准ARM7处理器技术的优势,简化了复杂SoC芯片的评估和开发工作。这套开发工具是一个完善的开发环境,包含构件组装、行为仿真、电路综合、布局、编
[新品]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved