请选择 进入手机版 | 继续访问电脑版
查看: 133|回复: 1

[分享] 关于内存屏障的那些事

[复制链接]

该用户从未签到

72

主题

80

帖子

0

金豆

社区小助手

Rank: 7Rank: 7Rank: 7

积分
720
最后登录
2019-5-20
发表于 2019-1-10 20:42:03 | 显示全部楼层 |阅读模式
QQ浏览器截图20190110203812.png


现实存在的问题

说起内存屏障(Memory Barrier),小编也总觉得和MCU的开发者相距甚远,比起LINUX内核的各种SMP、MMIO内存屏障的普遍应用来,MCU中内存屏障的使用少的可怜,MCU的开发者也基本不太重视内存屏障的问题,甚至没有意识到问题的存在。
然而,在最近遇到的MCU问题中,内存屏障问题,尤其是编译时内存重排序问题在使用GCC工具链的MCU开发中反复出现,而广大的开发者往往没有处理过这种问题而根本没有往这方面想,耗费了大量的调试时间。

内存访问指令的重排序

内存排序问题指的是CPU(ARM内核)访问存储器的顺序。
你在电脑上敲下的C代码经过编译变为汇编指令,直到汇编指令在CPU中运行,这整个过程中,代码访问内存的顺序有可能多次被重排序。这些重排序有可能是编译器在编译时导致产生汇编指令的重排序,也可能是CPU执行指令时产生的重排序。这些重排序旨在减少流水线延迟从而提高CPU的性能,当编译器或者CPU发现内存数据访问没有相关性时,这些重排序就有可能发生。
我们下面分别讲一下编译时的内存访问重排序和运行时的内存访问重排序。

编译时的内存访问重排序

我们知道,编译器做的工作是将人类可读的源码转换为CPU可读的指令,这个转换过程有一定的自由度,尤其是编译器优化等级较高时,编译器会分析依赖关系进行指令重排序。
依赖关系分为数据依赖和控制依赖,小编这里就不太多涉及理论了,只讲一个编译时重排序导致程序问题的例子。

  1. <p>Chip_DMA_Table是DMA的描述符数组,下面程序将串口DMA描述符赋值,并将配置DMA串口通道0控制寄存器。</p><p>Chip_DMA_Table[DMAREQ_USART0_TX].source  = DMA_ADDR(uartData + length);
  2. Chip_DMA_Table[DMAREQ_USART0_TX].dest    = DMA_ADDR(&(LPC_USART->FIFOWR));
  3. Chip_DMA_Table[DMAREQ_USART0_TX].next    = DMA_ADDR(0);
  4. Chip_DMA_Table[DMAREQ_USART0_TX].xfercfg = DMA_XFERCFG_CFGVALID | DMA_XFERCFG_SETINTA | DMA_XFERCFG_SWTRIG | DMA_XFERCFG_WIDTH_8 | DMA_XFERCFG_SRCINC_1 | DMA_XFERCFG_DSTINC_0 | DMA_XFERCFG_XFERCOUNT(length + 1);
  5. LPC_DMA->DMACH[DMAREQ_USART0_TX].XFERCFG = Chip_DMA_Table[DMAREQ_USART0_TX].xfercfg;</p>
复制代码


这段程序在某GCC版本-O2优化选项下,生成的重排序的汇编程序。为了可读性,小编把汇编语言“翻译”成C语言,如下面所示
  1. Chip_DMA_Table[DMAREQ_USART0_TX].xfercfg = DMA_XFERCFG_CFGVALID | DMA_XFERCFG_SETINTA | DMA_XFERCFG_SWTRIG | DMA_XFERCFG_WIDTH_8 | DMA_XFERCFG_SRCINC_1 | DMA_XFERCFG_DSTINC_0 | DMA_XFERCFG_XFERCOUNT(length + 1);
  2. LPC_DMA->DMACH[DMAREQ_USART0_TX].XFERCFG = Chip_DMA_Table[DMAREQ_USART0_TX].xfercfg;
  3. Chip_DMA_Table[DMAREQ_USART0_TX].source  = DMA_ADDR(uartData + length);
  4. Chip_DMA_Table[DMAREQ_USART0_TX].dest    = DMA_ADDR(&(LPC_USART->FIFOWR));
  5. Chip_DMA_Table[DMAREQ_USART0_TX].next    = DMA_ADDR(0);
复制代码


在上述程序中,Chip_DMA_Table[...].xfercfg与LPC_DMA->DMACH[...].XFERCFG在语句上就存在数据依赖,因此编译器不会改变这两个变量的相对访问顺序。

上述对LPC_DMA->DMACH中分量XFERCFG的赋值,意味着启动DMA操作,而其它语句是为了配置DMA操作的参数。如果把描述符中其他成员的赋值,重排序到XFERCFG的赋值之后,则显然DMA的操作不能正确地进行了。

这时,编译时指定内存屏障就可以帮助编译器理解数据访问的依赖性,保证在屏障后的数据访问不会被重新排序到屏障之前。

编译时设置内存屏障和编译器相关,GCC编译器中设置内存屏障的语句是asm volatile("" ::: "memory"); (小编也没听过关于编译时内存屏障在IAR或者KEIL的应用)。这段程序加上编译时内存屏障后变成下面这样。
  1. Chip_DMA_Table[DMAREQ_USART0_TX].source  = DMA_ADDR(uartData + length);
  2. Chip_DMA_Table[DMAREQ_USART0_TX].dest    = DMA_ADDR(&(LPC_USART->FIFOWR));
  3. Chip_DMA_Table[DMAREQ_USART0_TX].next    = DMA_ADDR(0);
  4. Chip_DMA_Table[DMAREQ_USART0_TX].xfercfg = DMA_XFERCFG_CFGVALID | DMA_XFERCFG_SETINTA | DMA_XFERCFG_SWTRIG | DMA_XFERCFG_WIDTH_8 | DMA_XFERCFG_SRCINC_1 | DMA_XFERCFG_DSTINC_0 | DMA_XFERCFG_XFERCOUNT(length + 1);
  5. asm volatile("" ::: "memory");
  6. LPC_DMA->DMACH[DMAREQ_USART0_TX].XFERCFG = Chip_DMA_Table[DMAREQ_USART0_TX].xfercfg;
复制代码


当然,为了避免编译时的访问内存的指令被重排序,除了考虑加入内存屏障,降低驱动程序的编译优化等级也许是更加简便的方法。当然,如果希望调整编译优化等级,也要通过检测汇编指令来进行确认。
值得提醒的时,编译时内存屏障只影响编译时的内存访问重排序,而对运行时的内存访问重排序没有作用。小编下面再讲讲运行时的内存访问重排序。

运行时的内存访问重排序

这个问题存在于Arm CPU核的设计中。
在ARMv7-M和ARMv6-M处理器中,程序的指令顺序不一定和执行顺序一致。在Arm的消息中心,官方解释了四点原因
处理器可以在不影响指令行为的前提下重新排序内存访问来改善程序效率
处理器存在多个总线接口
存储器和设备可能在不同的存储器地址分支上互联
一些存储器访问可能存在缓冲
在Arm的MPU配置中,内存访问属性可以被配置成为普通访问(Normal Access),设备访问(Device Access)和强顺序访问(Strongly-ordered access)。
内存访问属性可以通过MPU区域属性和大小寄存器(MPU Region Attribute and Size Register)中的TEX、S、C、B位设置。
当MPU没有设置时,存储器具有默认的内存访问属性,如下表所示
QQ浏览器截图20190110204019.png

ARMv7-M和ARMv6-M处理器根据存储访问类型有以下规则:
若存储器访问指令A1、A2,且在程序中,A1在A2之前。当A1,A2符合下表中由符号“<”表示所述的条件时,CPU保证先执行A1指令,后执行A2指令。
QQ浏览器截图20190110204025.png

由于CPU可以保证外部设备地址空间的访问顺序,对于应用开发者而言,普通访问的SRAM变量访问重排序,也就成为最需要关注的问题。
ARM提供了以下内存屏障用于内存读取或者存储的访问同步。
数据内存屏障(Data Memory Barrier) DMB
数据内存屏障可以保证程序中,在DMB之前的所有数据访问,在CPU运行时先于DMB之后的数据访问执行。
数据同步屏障(Data Synchronization Barrier)DSB
数据内存屏障可以保证程序中,在DSB之前的所有数据访问,在CPU运行时先于DSB之后的数据访问执行,且所有对系统控制区的访问,都会保证在DSB之前完成,在DSB完成之前,在DSB指令后面的任何指令都不可以执行。
指令同步屏障(Instruction Synchronization Barrier)ISB
ISB指令冲掉CPU流水线,程序中所有ISB之后的指令,只有在ISB执行完成后再取指。

回复

使用道具 举报

  • TA的每日心情
    开心
    昨天 17:38
  • 签到天数: 81 天

    [LV.6]常住居民II

    3

    主题

    172

    帖子

    0

    金豆

    高级会员

    Rank: 4

    积分
    529
    最后登录
    2019-5-19
    发表于 2019-1-11 16:14:52 | 显示全部楼层
    好文章  学习下
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    小黑屋|手机版|Archiver|恩智浦技术社区

    GMT+8, 2019-5-20 20:58 , Processed in 0.094126 second(s), 15 queries , MemCache On.

    Powered by Discuz! X3.4

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表