Cortex-M 实现互斥操作的三种方法
注:本文仅针对Cortex-M3/4 系列进行讲述。
在传统的ARM处理器架构中,常使用SWP指令来实现锁的读/写原子操作,但从ARM v6开始,读/写访问在独立的两条总线上进行,SWP指令已无法在此架构下保证读/写访问的原子操作,因此互斥访问指令应运而生。本文结合项目中运用的相关方法,总结Cortex-M芯片常用的互斥访问方法。
互斥访问方式1--LDREX/STREX指令
ARM支持的互斥指令对有LDREX/STREX、LDREXB/STREXB 及 LDREXH/STREXH(专有的寄存器加载/存储指令),其分别支持字/字节/半字访问,本节以LDREX/STREX为例.
语法格式
LDREX{cond} Rt, [Rn {, #offset}]
STREX{cond} Rd, Rt, [Rn {, #offset}]
其中
cond: 可选状态码-若指令包含此状态码,则只有当APSR寄存器中的状态位满足状态码条件时,指令才会执行
Rd: 目的寄存器-指令执行后的返回状态,0执行成功,1执行失败
Rt: 待加载/存储的寄存器
Rn: 寄存器地址
offset: 可选的地址偏移
基本要求
使用互斥访问指令时,需满足以下基本要求,以防不可预期的结果出现。
1. LDREX/STREX必须成对出现
2. LDREX/STREX的Rn寄存器地址必须一致,操作的寄存器长度必须一致
3. LDREX/STREX之间不得使用PC指针,操作的寄存器不使用SP指针
4. LDREX/STREX之间的指令要尽可能的简短,offset需4字节对齐,范围在0~1020之间(不同的厂商设置范围不同)
互斥写失败情况
在满足基本要求后,互斥写不一定成功,如互斥操作中途遇到以下情况:
1. 调用CLREX指令清除互斥状态
2. 发生上下午切换(如中断)
3. 之前未执行过LDREX
4. 总线反馈的互斥错误
使用方法
以nRF52源码中的 nrf_atomic_internal_orr() 函数为例,该函数实现了或运算的原子操作,其中p_ptr为初始值,value为或运算因子,p_new为运算后的值,函数返回原为子操作之前的p_ptr的值。

先简单描述上述各行代码:
89: r0/r1/r2分别存储的p_ptr/value/p_new的值
94:将p_ptr地址付给r4
97:将r4所指向的值赋给r0,r0获得了p_ptr此时的值
98:对r0存储的值进行或运算,运算值赋给r5
99:将r5的值存储给r4指向的地址,即更新p_ptr的值,同时将本条指令的执行结果赋给r3
100/101:判断返回值r3,若不为0,重试 97~99的操作
103/104/105:将运算值赋给r2指向的值,即得到新值
代码的关键在97行,需注意的是,当函数执行结束返回时,r0存储函数的返回值,因此此函数的返回值为原子操作之前的p_ptr值,而不是调用此函数时传入的p_ptr值(中途可能有变)
以实际场景为例,假若存在两个任务A和B,以及一个共享内存Mem,互斥变量Flag标记Mem是否正在被占用(0:空闲中,1:占用中),要如何实现呢?
情况1. A/B先后访问Mem,则
1. A首先调用 nrf_atomic_internal_orr() 函数(Flag=0),尝试原子操作,此时R0=0,执行结束后,由返回值R0可知,Flag成功由0->1,A占用Mem成功
2. 此时发生任务切换
3. B调用 nrf_atomic_internal_orr() 函数(Flag=1),尝试原子操作,此时R0=1,执行结束后,由返回值R0可知,Flag在置位之前已经是1,B占用Mem失败
注:因为只有A/B先后访问nrf_atomic_internal_orr()函数,因此各自只需要尝试一次原子操作即可成功。
情况2.A/B同时访问Mem,A在原子操作过程中被B抢占,则
1. A首先调用 nrf_atomic_internal_orr() 函数(Flag=0),尝试第一次原子操作,此时R0=0,此时发生任务切换
2. A被抢占,上下文切换退出
3. B调用 nrf_atomic_internal_orr() 函数(Flag=0),尝试第一次原子操作,此时R0=0,执行结束后,由返回值R0可知,Flag成功由0->1,B占用Mem成功
4. 此时发生任务切换
5. A继续执行第一次原子操作,因在LDREX/STREX之间已发生上下文切换,此次原子操作STREX返回 1,执行失败
6. A继续执行第二次原子操作,注意:此时R0重载,R0=1,执行结束后,由返回值R0可知,Flag在置位之前已经是1,A占用Mem失败
因此本例中,调用nrf_atomic_internal_orr() 执行原子操作后,通过判断函数返回值可知,本次互斥操作是否抢占资源成功。
互斥访问方式2--Bit-Band操作
在支持 “locked transfers”或仅有单个总线主机的内存系统中,使用位带操作也可实现信号量操作。要实现互斥访问某个资源,操作过程中需遵循以下几点:
1. 系统为每个需互斥访问的任务分配一个位带bit位,
2. 任务仅能对自己的bit位进行读-修改-写操作。
2. 不能以常规的写方式来直接修改位带区域值,否则可能丢失已锁定的位信息
具体操作过程直接上图:

优点:可使用C代码直接实现上述互斥访问逻辑。
互斥访问方式3--关中断
最为简单粗暴的互斥访问方法,FreeRTOS的信号量获取/释放操作便采用此方式进入临界区。

关中断实现起来虽然简单,但也需根据具体场景来选择关总中断还是外设中断,否则可能降低系统的实时性甚至造成数据丢失。
举例来说,在之前经历的一个项目中,有一款MCU既需要负责USB数据的收发,同时还得处理无线数据的转发,如在处理USB临界区数据时选择关总中断,则可能导致无线数据无法及时处理甚至导致丢包,在该场景下,若选择只关闭USB中断,则MCU依然能够在实现局部互斥操作的同时实时响应优先级更高的事件。
参考手册
TI 《Cortex-M3/M4F Instruction Set》
宋岩 《Cortex -M3 权威指南》
《The Definitive guild to the ARM Cortex-M3》Second Edition》
《The Definitive guild to the ARM Cortex-M3 and Cortex-M4 Processors》Third Edition
Cortex-M 实现互斥操作的三种方法的更多相关文章
- PHP实现链式操作的三种方法详解
这篇文章主要介绍了PHP实现链式操作的三种方法,结合实例形式分析了php链式操作的相关实现技巧与使用注意事项,需要的朋友可以参考下 本文实例讲述了PHP实现链式操作的三种方法.分享给大家供大家参考,具 ...
- python对mysql数据库操作的三种不同方式
首先要说一下,在这个暑期如果没有什么特殊情况,我打算用python尝试写一个考试系统,希望能在下学期的python课程实际使用,并且尽量在此之前把用到的相关技术都以分篇博客的方式分享出来,有想要交流的 ...
- JAVA之线程同步的三种方法
最近接触到一个图片加载的项目,其中有声明到的线程池等资源需要在系统中线程共享,所以就去研究了一下线程同步的知识,总结了三种常用的线程同步的方法,特来与大家分享一下.这三种方法分别是:synchroni ...
- mysql分表的三种方法
先说一下为什么要分表当一张的数据达到几百万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目的就在于此,减小数据库的负担,缩短查询时间.根据个人经验,mysql执行一 ...
- Linux系统下修改环境变量PATH路径的三种方法
这里介绍Linux的知识,比如把/etc/apache/bin目录添加到PATH中有三种方法,看完之后你将学会Linux系统下如何修改环境变量PATH路径,需要的朋友可以参考下 电脑中必不可少的就是操 ...
- 【转】asp.net导出数据到Excel的三种方法
来源:http://www.cnblogs.com/lishengpeng1982/archive/2008/04/03/1135490.html 原文出处:http://blog.csdn.net/ ...
- iis 重启 (三种方法)
iis 重启 (三种方法) WINDOWS提供WEB服务的IIS有时候会出现访问过大导致网站打不开,这时重启IIS是最好的选择. 方法/步骤 1 1.界面操作 打开“控制面板”->“管理工具”- ...
- python面对对象编程------3:写集合类的三种方法
写一个集合类的三种方法:wrap,extend,invent 一:包装一个集合类 class Deck: def __init__( self ): self._cards = [card6(r+1, ...
- Tomcat 部署项目的三种方法
1.下载 Tomcat 服务器 ①.官网下载地址:http://tomcat.apache.org/ ②.tomcat 8.0 64位百度云下载地址:http://pan.baidu.com/s/1s ...
随机推荐
- java集合框架之LinkedList
参考http://how2j.cn/k/collection/collection-linkedlist/370.html LinkedList 与 List接口 与ArrayList一样,Linke ...
- bzoj 2500 幸福的道路 树上直径+set
首先明确:树上任意一点的最长路径一定是直径的某一端点. 所以先找出直径,求出最长路径,然后再求波动值<=m的最长区间 #include<cstdio> #include<cst ...
- BZOJ_3207_花神的嘲讽计划Ⅰ_哈希+主席树
BZOJ_3207_花神的嘲讽计划Ⅰ_哈希+主席树 Description 背景 花神是神,一大癖好就是嘲讽大J,举例如下: “哎你傻不傻的![hqz:大笨J]” “这道题又被J屎过了!!” “J这程 ...
- BZOJ_1260_[CQOI2007]涂色paint _区间DP
BZOJ_1260_[CQOI2007]涂色paint _区间DP 题意: 假设你有一条长度为5的木版,初始时没有涂过任何颜色.你希望把它的5个单位长度分别涂上红.绿.蓝.绿.红色,用一个长度为5的字 ...
- python struct.pack() 二进制文件,文件中打包二进制数据的存储与解析
学习Python的过程中,遇到一个问题,在<Python学习手册>(也就是<learning python>)中,元组.文件及其他章节里,关于处理二进制文件里,有这么一段代码的 ...
- 使用FileUpload实现Servlet的文件上传
简介 FileUpload 是 Apache commons下面的一个子项目,用来实现Java环境下的文件上传功能. FileUpload链接 FileUpload 是基于Apache的Commons ...
- web版仿微信聊天界面|h5仿微信电脑端案例开发
前几天开发了一款手机端h5仿微信聊天,人唯有不停学习才能进步,这段时间倒腾着整理了下之前项目,又重新在原先的那版基础上开发了一款仿微信聊天电脑端web版本,聊天页面又重新优化了多图预览.视频播放,右键 ...
- monkey------模块组合测试
由于项目基本功能和预置APK都很多,单个模块跑消耗机器数量很大,效果也不佳.而且monkey测试经常要过夜测试,所以组合模块试用较多,而且发现问题量也更大.组合模块就是按照测试标准要求和模块特性,按照 ...
- JAVA线程及简单同步实现的原理解析
线程 一.内容简介: 本文主要讲述计算机中有关线程的相关内容,以及JAVA中关于线程的基础知识点,为以后的深入学习做铺垫.如果你已经是高手了,那么这篇文章并不适合你. 二.随笔正文: 1.计算机系统组 ...
- 改造MIP获得搜索青睐,轻松完成SEO
搜索引擎目标及页面排序方法 搜索引擎作为互联网流量的入口,承担着流量分发的职责.但排序成千上万的网页,决定哪些网页在第一页,是由网页本身的用户体验决定的.权重算法会从内容优质性,广告多少,加载速度等多 ...