最近工作中遇到一个问题,大致描述一下:

我们SOC用的arm cortex m7内核,在设计时设计人员图方便,将SPI controller的寄存器(即原本应该是APB空间)放在了0x60000000的某一块空间(此空间arm的memory定义区间为external memory),然后同时把SPI flash的存储空间也映射在了0x60000000的这一块区间内。后者将spi flash memory映射在此区间内其实时合理的。但是将spi controller的寄存器也放在这块空间,验证人员只验证了对寄存器的访问,是可以的,此时并没有发现什么问题。

说明一下,spi controller用的synopsis的design ware IP:SSI。

SSI要求在设置一些配置寄存器前必须先disable SSI模块,所以软件先对该寄存器设置,即对使能寄存器下disable=0,然后开始对一些配置寄存器对配置设定,比如位宽,clock divider等。注意有个前提,SOC powerup之后,使能寄存器默认是enable的。!所以还必须提前disable动作。

debug软件时发现,在disable后的第一个配置寄存器的设定总是写不进去!

后来通过验证simulation发现,在CPU出来的bus上,disable寄存器的设定竟然在第一个配置寄存器设定的后面,可是软件明明时先设定的disable。所以导致第一个配置寄存器的配置软件这边始终看不到生效!!!!

至于原因,我们猜测时arm cortex-m7 cpu认为0x60000000区域是external memory区域,再加上cm7支援乱序发射双发射等等因素,导致cpu往spi controller发送是按照了memory属性,可能乱序store了。这种情况在APB总线上应该是不可能发生的,不过验证人员没有继续追究?!!待后面补吧。

这其实是CPU出来的AXI总线和CPU指令乱序有关的问题,不像是编译器乱序的问题。!

解决办法: 我在disable寄存器设定后面加了__DSB()指令就可以了。!!!即让DSB指令前面的所有指令完成后再执行后面的指令,即数据同步屏障指令!我估计__DMB()和__ISB()也是可以的。

【转】现代 CPU中指令的执行次序不一定按顺序执行,没有相关性的指令可以打乱次序执行,以充分利用 CPU的指令流水线,提高执行速度。同时,编译器也会对指令进行优化,例如,调整指令顺序来利用CPU的指令流水线。这些优化方式,大部分时候都工作良好,但是在一些比较复杂的情况可能会出现错误,例如,执行同步代码时就有可能因为优化导致同步原语之后的指令在同步原语前执行。

内存屏障和编译屏障就是用来告诉CPU和编译器停止优化的手段。编译屏障是指使用伪指令“memory”告诉编译器不能把“memory”执行前后的代码混淆在一起,这时“memory”起到了一种优化屏障的作用。内存屏障是在代码中使用一些特殊指令,如ARM中的dmb、dsb和isb指令,x86中的sfence、lfence和mfence指令。CPU遇到这些特殊指令后,要等待前面的指令执行完成才执行后面的指令。这些指令的作用就好像一道屏障把前后指令隔离开了,防止CPU把前后两段指令颠倒执行。

(1)ARM平台的内存屏障指令。

dsb:数据同步屏障指令。它的作用是等待所有前面的指令完成后再执行后面的指令。

dmb:数据内存屏障指令。它的作用是等待前面访问内存的指令完成后再执行后面访问内存的指令。

isb:指令同步屏障。它的作用是等待流水线中所有指令执行完成后再执行后面的指令。

(2)x86平台上的内存屏障指令。

sfence:存储屏障指令。它的作用是等待前面写内存的指令完成后再执行后面写内存的指令。

lfence:读取屏障指令。它的作用是等待前面读取内存的指令完成后再执行后面读取内存的指令。

mfence:混合屏障指令。它的作用是等待前面读写内存的指令完成后再执行后面读写内存的指令。

要精确地理解这些指令的含义,需要去查阅处理器的说明。这里只是对它们做了一点简单的介绍。下面看看Android是如何利用这些指令来实现内存屏障和编译屏障的:

1.ARM平台的函数代码

(1)编译屏障:

编译屏障即在代码前后加如下代码,编译器就不会将此区间的内存访问乱序操作
void android_compiler_barrier()
{  asm_volatile_("" : : : "memory");
} 

编译屏障的实现只是使用了伪指令memory。

(2)内存屏障:

void android_memory_barrier()
{
#if ANDROID_SMP == 0  android_compiler_barrier();
#else  __asm__volatile_("dmb" : : : "memory");
#endif
}  void android_memory_store_barrier()
{
#if ANDROID_SMP == 0  android_compiler_barrier();
#else  __asm_volatile_("dmb st" : : : "memory");
#endif
} 

内存屏障的函数中使用了宏ANDROID_SMP。它的值为0时表示是单CPU,这种情况下只使用编译屏障就可以了。在多CPU情况下,同时使用了内存屏障指令“dmb”和编译屏障的伪指令“memory”。函数android_memory_store_barrier()中的dmb指令还使用了选项st,它表示要等待前面所有存储内存的指令执行完后再执行后面的存储内存的指令。

2.x86平台下的函数代码

(1)编译屏障:

void android_compiler_barrier(void)
{  __asm__ __volatile__ ("" : : : "memory");
} 

和ARM平台下一样,编译屏障的实现只是使用了伪指令memory。

(2)内存屏障:

#if ANDROID_SMP == 0
void android_memory_barrier(void)
{  android_compiler_barrier();
}  void android_memory_store_barrier(void)
{  android_compiler_barrier();
}
#else
void android_memory_barrier(void)
{  asm__volatile_("mfence" : : : "memory");
}
void android_memory_store_barrier(void)
{  android_compiler_barrier();
}
#endif 

x86平台也一样,如果是单CPU,内存屏障的实现只使用了编译屏障。在多CPU情况下,函数android_memory_barrier()使用了CPU指令“mfence”,对读写内存的情况都进行了屏障。但是android_memory_store_barrier()函数只使用了编译屏障,这是因为Intel的CPU不对写内存的指令重新排序。所以不需要内存屏蔽指令。

真正的屏障指令的应用场景举例:

①  ARM中断程序中,如:

// 中断处理函数

void INT_LED_HANDLER(void)
{/* Clear interrupt flag.*/INT_ClearStatusFlags(PIT, kPIT_Chnl_0, kPIT_TimerFlag);pitIsrFlag = true;   __DSB();
}

程序通过中断信号进入中断处理函数时,首先应当清除相应的中断标志位,但有些CPU的时钟太快,快于中断使用的时钟,就会出现清除中断标志的动作还未完成,CPU就又一次重新进入同一个中断处理函数,导致死循环,__DSB() 指令的作用就是避免上述情况的发生。
__DSB()的原型是:

__STATIC_FORCEINLINE void __DSB(void)
{__ASM volatile ("dsb 0xF":::"memory");
}

可以直接调用__DSB(),在CMSIS的头文件中有定义。上面的定义原型可知,__DSB()包括了编译屏障和指令屏障两种措施。

如果只是编译屏障,可以调用CMSIS头文件中的如下定义,即相对于__DSB()只有存memory编译关键字。

/* cmsis_armcc.h */#ifndef   __COMPILER_BARRIER#define __COMPILER_BARRIER()                   __memory_changed()
#endif等价于/* gcc */
__asm__ __volatile__("": : :"memory")

② 多CPU系统内存访问,参考下一篇

一、barrier指令DSB,DMB,ISB,fence——内存屏障,指令屏障相关推荐

  1. RAM汇编指令DMB、DSB、ISB、SEV等

    最近用keil调试STM32时,在代码中遇到了一些汇编指令,如DMB.DSB.ISB.SEV,现总结如下: DMB.DSB.ISB.SEV等指令都属于RAM汇编指令,在<ARM Cortex-M ...

  2. ARM64中的内存屏障指令

    内存屏障指令是系统编程中很重要的一部分,特别是在多核并行编程中.本章重点介绍内存屏障指令产生的原因.ARM64处理器内存屏障指令以及内存屏障的案例分析等内容. 18.2.1 使用内存屏障的场景 在大部 ...

  3. ARM指令寻址方式之: 内存访问指令寻址

    4.2  内存访问指令寻址 根据内存访问指令的分类,内存访问指令的寻址方式可以分为以下几种. ① 字及无符号字节的Load/Store指令的寻址方式. ② 杂类Load/Store指令的寻址方式. ③ ...

  4. X86汇编语言从实模式到保护模式01:处理器、内存和指令

    目录 1. 如何构造自动取指执行的处理器 1.1 开关构成指令与数据 1.2 内存存储指令与数据 1.3 自动取指执行 1.4 处理器基本结构 2. 程序重定位与内存分段机制 2.1 程序重定位问题的 ...

  5. ARM体系架构—ARMv7-A指令集:内存操作指令

    ARM体系架构-ARMv7-A指令集 一.ARMv7-A指令集 一.ARMv7-A内存操作指令 二.单寄存器寻址内存操作指令 三.多寄存器寻址内存操作指令 四.SWP,SWPB 一.ARMv7-A指令 ...

  6. 指令隔离DMB,DSB,ISB

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

  7. 【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 ...

  8. DMB DSB和ISB区别

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

  9. DBG、DMB、DSB 和 ISB

    调试指令.数据内存屏障指令.数据同步屏障指令和指令同步屏障指令. DBG 调试提示可向调试系统及其相关系统发送提示. 有关这些系统如何使用此指令的信息,请参这些系统的文档. DMB 数据内存屏障可作为 ...

最新文章

  1. mysql文章浏览计数_高并发文章浏览量计数系统设计
  2. IAP的原理和stm8的IAP
  3. 游标 每天给每个用户发钱
  4. swift不用声明类型那些事
  5. vue组件的基本使用:入门示例
  6. 对数据仓库进行数据建模_确定是否可以对您的数据进行建模
  7. STMF4x 固件库V1.25.0
  8. 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 25丨求团队人数【难度中等】​
  9. strtotime的几种用法区别
  10. 【白皮书分享】2022年私域运营趋势及创业机会.pdf(附下载链接)
  11. LVS DR模型详解
  12. Ubuntu16.04编译Android5.1源码
  13. 软考软件设计师中级考试免费视频教程汇总
  14. mysql slave 'system user' locked_Mysql的slave lags一例
  15. hikari数据源配置类_SpringBoot2集成Mybatis Hikari多数据源配置
  16. 在米帝求学的你,知道每个州的圣诞节特色活动吗!
  17. python 发送outlook邮件(html)格式需要避的坑
  18. ppt(office365)之起始页、目录页、结束页界面布局技巧
  19. JavaScript----<script></script>
  20. 合作单位倒闭,24000回收电脑43台,老板爽快成交,是我报高了?

热门文章

  1. 还不到4折:赶紧来抢券啊!!!
  2. 欧几里德算法(Euclidean algorithm)
  3. 计算机文化基础0008 17秋在线作业1,【在线】《计算机文化基础0008》17秋在线作业2.doc...
  4. 小米公司开源 MIUI 6 第三方适配工具 『Patchrom』
  5. sublime text 3 插件 OmniMarkupPreviewer 报404解决办法
  6. 新浪短链接生成器 最新新浪短网址短链接在线生成器推荐
  7. 轻量级封装DbUtilsMybatis之四MyBatis主键
  8. 抖音SEO优化:最详细抖音视频SEO教程
  9. 884.两句话中的不常见单词
  10. 提高计算机软件速度的方法,小白看过来!提高电脑速度8种实用方法