像ARM7TDMI这样经典的ARM处理器会按照程序的顺序来执行指令或访问数据。而最新的ARM处理器会对执行指令和访问数据的顺序进行优化。举个例子,ARM v6/v7的处理器会对以下指令顺序进行优化。

  1. LDR r0, [r1] ; 从普通/可Cache的内存中读取,并导致cache未命中
  2. STR r2, [r3] ; 写入普通/不可Cache的内存

假设第一条LDR指令导致Cache未命中,这样Cache就会填充行,这个动作一般会占用好几个时钟周期的时间。经典的ARM处理器(带Cache的),比如ARM926EJ-S会等待这个动作完成,再执行下一条STR指令。而ARM v6/v7处理器会识别出下一条指令(STR)并不需要等待第一条指令(LDR)完成(并不依赖于r0的值),于是就会先执行STR指令,而不是等待LDR指令完成。

在有些情况下,类似上面提到的这种推测读取或者乱序执行的处理器优化并不是我们所期望的,因为可能使程序不按我们的预期执行。在这种情况下,就有必要在需要严格的、“类经典ARM”行为的程序中插入内存隔离指令。ARM提供了3种内存隔离指令。简单起见,以下的描述都是在单处理器环境下。

l  数据内存隔离(DMB):在DMB之后的显示的内存访问执行前,保证所有在DMB指令之前的内存访问完成。

l  数据同步隔离(DSB):等待所有在DSB指令之前的指令完成(之后再执行后续的指令,译注)。

l  指令同步隔离(ISB):清除(flush)流水线,使得所有ISB之后执行的指令都是从cache或内存中获得的(而不是流水线中的,译注)。

[译注]:DMB与DSB的区别在于DMB可以继续执行之后的指令,只要这条指令不是内存访问指令。而DSB不管它后面的什么指令,都会强迫CPU等待它之前的指令执行完毕。而ISB不仅做了DSB所做的事情,还将流水线清空。

需要注意,ARM v6中的CP15等价隔离指令在ARM v7中是弃用的。因此,可能的话,建议任何使用这些指令的代码应该改用以上3条新的隔离指令。

互斥量

以下情况软件必须使用DMB:

l  在请求资源期间,比如通过锁定一个互斥量或减少信号量以及任何形式的对资源的访问。

l  在使资源可用之前,比如通过解锁一个互斥量或增加信号量。

下面是一个阻塞互斥量的实现例子

l  lock_mutex请求一个互斥量并阻塞直到请求到资源。如果阻塞了,在重试之前它会等待事件唤醒(通过WFE)。

l  unlock_mutex释放一个互斥量并发送一个事件来通知等待的“进程”。

  1. LOCKED EQU 1
  2. UNLOCKED EQU 0
  3. lock_mutex
  4. ; 互斥量是否锁定?
  5. LDREX r1, [r0] ; 检查是否锁定
  6. CMP r1, #LOCKED ; 和"locked"比较
  7. WFEEQ ; 互斥量已经锁定,进入休眠
  8. BEQ lock_mutex ; 被唤醒,重新检查互斥量是否锁定
  9. ; 尝试锁定互斥量
  10. MOV r1, #LOCKED
  11. STREX r2, r1, [r0] ; 尝试锁定
  12. CMP r2, #0x0 ; 检查STR指令是否完成
  13. BNE lock_mutex ; 如果失败,重试
  14. DMB ; 进入被保护的资源前需要隔离,保证互斥量已经被更新
  15. BX lr
  16. unlock_mutex
  17. DMB ; 保证资源的访问已经结束
  18. MOV r1, #UNLOCKED ; 向锁定域写"unlocked"
  19. STR r1, [r0]
  20. DSB ; 保证在CPU唤醒前完成互斥量状态更新
  21. SEV ; 像其他CPU发送事件,唤醒任何等待事件的CPU
  22. BX lr

DSB指令保证了在发送事件之前,同步变量已经被更新了。

内存重映射

当复位服务程序或启动代码在flash(ROM)中,它们被映射在地址0x0处来保证程序能正确地从向量表中启动,通常它们驻留在内存的底部。如下图左侧所示。

在系统初始化之后,你可能希望关闭flash的映射,这样就可以将底部的位置给RAM使用(如上图右侧)。下面的代码(永远在Flash中运行)在运行内存复制例程将一些数据复制到内存底部(RAM)前关闭了flash的映射。

  1. MOV r0, #0
  2. MOV r1, #REMAP_REG
  3. STR r0, [r1] ; 关闭flash映射
  4. DMB ; 保证STR完成
  5. BL block_copy_routine() ; 复制代码到RAM
  6. ISB ; 保证流水线清空
  7. BL copied_routine() ; 运行复制后的代码 (RAM中)

如果没有STR和BL中间的DMB指令,就不能保证这条STR指令在复制代码到底部内存之前已经完成,因为复制例程可能会在通过STR写入的数据还在写缓冲(Write Buffer)中时就运行。DMB指令强迫所有DMB之前的数据访问完成。而ISB指令防止了在复制代码结束之前就从RAM中取指令。

中断

下面的框图表示了包含有基于通用中断控制器(GIC)的系统结构。当中断控制器检测到中断发生时,它会发送nIRQ信号给处理器。这会触发一系列事件包括处理器运行中断处理例程,并且屏蔽IRQ中断源(忽略接着到来的nIRQ的变化)。

下面的中断服务例程通过读取中断确认寄存器(IAR)来确认中断。例程不仅返回挂起中最高优先级的中断ID,还告诉中断控制器解除nIRQ的信号。在这之后,中断服务例程需要重新使能中断,以使更高等级的中断能抢占当前的中断。

  1. interrupt_handler
  2. ; ...
  3. LDR r0, =GIC_CPUIF_BASE ; 获得中断控制器基地址
  4. LDR r1, [r0, #0x0c] ; 读取IAR,同时解除nIRQ信号
  5. DSB ;确保内存访问结束,并且没有其他的指令运行
  6. ; 继续运行之前
  7. CPSIE i ; 重新允许中断
  8. ; ...
  9. RFE sp! ; 从服务程序返回

在这个过程中,正确的操作需要在CPSIE使能之前完成IAR的读取。因为这两条指令之间没有数据依赖,所以CPU会在LDR完成之前就执行CPSIE。这会导致处理器再次响应相同的中断(中断允许,且nIRQ没有解除,译注)。因此应该在这两条命令中插入一条DSB指令。

自修改代码

自修改代码必须在ISB之后运行,因为内核流水线中可能包含过期的指令。

下面的例子演示了一段将ROM中的代码搬运到RAM中,并跳转过去执行的代码。

  1. Overlay_manager
  2. ; ...
  3. BL block_copy ; 将新例程从ROM复制到RAM
  4. B relocated_code ; 跳转到新例程

如果你使能了分支预测,并且像上面的例子一样重定位代码,处理器会预测到第二条分支指令即将执行,然后从其指示例程中取指。这就会导致处理器运行旧的例程。如果复制例程和跳转到新例程的分支指令离得很近,这样的问题就会发生。

为了确保这样的优化不要发生,你必须在新的重定位过的代码运行前插入一条ISB指令,以此来保证预取指缓冲在处理器重新取指前已经被清除:

  1. Overlay_manager
  2. ; ...
  3. BL block_copy ; 将新例程从ROM复制到RAM
  4. ISB ; 保证流水线被清除
  5. B relocated_code ; 跳转到新例程

如果你正在复制的内存被设置为了“写回型可cache的”,那么你应该清理(clean)cache以保证数据已经写入到了主存中。并且,指令cache应该被设置为无效,这样处理器就不会执行其他“被cache”的指令了。

  1. Overlay_manager
  2. ; ...
  3. BL block_copy ; 将新例程从ROM复制到RAM
  4. DMB ; 保证内存访问结束
  5. data_cache_clean ; 清理cache保证新例程已经被写入RAM
  6. instruction_cache_invalidate ; 将指令cache设置为无效,这样旧指令就不会再被cache
  7. DSB ; 在内存访问前,清理并使cache无效
  8. ISB ; 保证流水线被清除
  9. B relocated_code ; 跳转到新例程

类似的需要隔离的地方:

l  JIT(Just-In-Time)编译器,比方说将Jazelle字节码转换为ARM代码。

l  Post链接器或加载器,在运行时将代码重定位到内存中。

原文链接:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka14041.html

指令隔离DMB,DSB,ISB相关推荐

  1. ARM指令之精髓DMB,DSB,ISB指令

    4.3.5 汇编语言:指令隔离(barrier)指令和存储器隔离指令 CM3 中的另一股新鲜空气是一系列的隔离指令(亦可以译成"屏障"."路障",可互换使用-- ...

  2. DMB DSB ISB 简介

    DMB: Data memory barrier 理解DMB指令,先看下面例子,在core 0和core1上同时跑两个不同的指令(如下表所示) core 0 core 1 Write A; Load ...

  3. 一、barrier指令DSB,DMB,ISB,fence——内存屏障,指令屏障

    最近工作中遇到一个问题,大致描述一下: 我们SOC用的arm cortex m7内核,在设计时设计人员图方便,将SPI controller的寄存器(即原本应该是APB空间)放在了0x60000000 ...

  4. DMB DSB和ISB区别

    DMB: Data memory barrier 理解DMB指令,先看下面例子,在core 0和core1上同时跑两个不同的指令(如下表所示) core 0 core 1 Write A; Write ...

  5. AngularJS自定义指令–隔离范围教程

    Earlier we looked at different directive properties and created a simple directive using those prope ...

  6. 【ARM Cache 入门及渐进五--内存屏障ISB/DSB/DMB】

    文章目录 Cache 之内存屏障指令 1.1 内存屏障基本规则 1.2 DMB(数据存储屏障) 1.2.1 DMB 使用场景 1.3 DSB(数据同步屏障) 1.3.1 DSB 使用背景 1.3.2 ...

  7. ARMV8 datasheet学习笔记3:AArch64应用级体系结构之Memory order

    1.前言 2.基本概念 Observer 可以发起对memory read/write访问的都是observer; Observability 是一种观察能力,通过read可以感知到别的observe ...

  8. Linux并发与同步专题 (1)原子操作和内存屏障

    关键词:. <Linux并发与同步专题 (1)原子操作和内存屏障> <Linux并发与同步专题 (2)spinlock> <Linux并发与同步专题 (3) 信号量> ...

  9. [ARM-assembly]-ARMv8-A64指令集总结和学习

    ★★★ 个人博客导读首页-点击此处 ★★★ 文章目录 1.一个简单的aarch64架构图 2.A64指令集的特点: 3.跳转指令 (1).条件跳转指令 (2).无条件跳转label指令 (3).无条件 ...

最新文章

  1. 不使用加减乘除法,完成两个数的加法
  2. 基于SSM开发实现中药制剂网站系统
  3. linux debian 自动安装,debian系统精简安装
  4. WIN32 _WIN32 _WIN64宏定义
  5. 黄聪:如何使用CodeSmith批量生成代码(原创系列教程)
  6. MyEclipse设置选中单词其它同名单词前景色和背景色
  7. [转载] JAVA环境变量配置
  8. 一线大厂在用的反爬虫方法,看我如何破了它!
  9. ipad 开发常用问题
  10. Freemarker输出$和html标签等特殊符号
  11. 【优化算法】鼠群优化算法(RSO)【含Matlab源码 1462期】
  12. 职称计算机 将计算机broad_1下的e盘映射为k盘网络驱动器,职称计算机考试网络基础答案(1)...
  13. android fastboot模式下载以及出现的问题
  14. 互联网热点自动获取工具的实现
  15. STM32CUBE——使用DWT提供毫秒延迟
  16. 用python爬取微博评论对鹿晗、关晓彤微博进行情感分析
  17. 重新定义RPA,重塑智能生产力 | 云扩科技RPA论坛成功举办
  18. HDU 6078 Wavel Sequence
  19. linux操作系统2试题,linux操作系统试题
  20. 信创办公--基于WPS的Word最佳实践系列(快速自定义访问工具栏)——以添加“输出为PPTX”命令为例

热门文章

  1. python里面的报错语句翻译_翻译《Writing Idiomatic Python》(二):函数、异常
  2. 高逼格技能教你玩转Excel
  3. utc时间 单位换算_UTC转换本地时间
  4. html table的边框线怎么变圆角_CSS如何设置html table表格边框样式
  5. 2018服务机器人发展现状及2019趋势分析
  6. GitHub Actions工作流语法
  7. python snmp_cmds库snmpwalk 中文正常显示方法
  8. 路由来源、优先级和度量值
  9. Ext.js 自定义桌面注意
  10. 通过瑞利判据对显微镜物镜进行分辨率研究