1、数据一致性是一个重要的问题,它定义了不同的CPU、系统总线所有的master看到的是相同的一片内存。

2、因为cache的存在,以及编译器对某些C语言语句的优化,使得CPU对某个内存变量的修改不能立刻更新到内存,或者其他系统的master修改了内存变量,但是CPU仍然使用cache中的值或者寄存器中的值来代表变量,此时就发生了数据一致性的问题:不同的系统总线master对同一个变量看到不同的值(CPU也可以看做是系统总线的master)。

3、先看看编译器优化对数据一致性的影响:

有如下语句:

int i = 0;

while(1)

{

i++;

if(i > DELAY)

break;

}

假设上述代码片段用来实现延时,或者其他功能。此时编译器会将变量i的值读入CPU内部寄存器,初始化为0,在while循环体中,对i++的操作就是对寄存器的操作:

MOV R7,#+0

LDR R6,=0xFF

B ??main_0

??main_0:

ADD R7,R7,#+1

CMP R7,R6

BLT ??main_1

以上是ARM中对应的汇编语言。可以看到编译器使用R7来保存i,R6来保存DELAY(值为0xFF)常量,然后在while循环中,只是对存i变量的寄存器R7加1,并没有对i变量的内存操作。如果其他CPU,或者总线master依赖于变量i来控制一些功能,此时就会出错,因为i的最新值只是存在于CPU寄存器中。

针对这样的情况,我们可以使用volatile关键字告诉编译器,对i变量的读写,每次都要老老实实地从内存取,并且修改后,还要马上更新到内存:

volatile int i = 0;

while(1)

{

i++;

if(i > DELAY)

break;

}

对应的汇编语言是:

MOV R1,#+0

STR R1,[SP, #+0]

LDR R6,=0xFF

B ??main_0

??main_0:

LDR R0,[SP, #+0]

ADD R0,R0,#+1

STR R0,[SP, #+0]

LDR R0,[SP, #+0]

CMP R0,R6

BLT ??main_1

上面的汇编语言中,R6是常量DELAY的值0xFF,而SP是变量i的内存地址。由此可见,每次都是先SP指向的内存中(即i的地址)LDR到R0,R0++,然后再将R0的值更新到SP指向的内存中(即i的内存位置)。判断i是否大于DELAY时,也是先将i的值从SP指向的地址中LDR到R0,然后在和R6(DELAY的值)比较大小。

4、再看看cache对数据一致性的影响

如果开启了数据cache,那么CPU对内存的读写都要经过cache缓冲。读就是读cache,写也是写cache。

考虑一下情况:CPUwhile循环退出依赖于一个内存地址的值,并且这个内存地址的值由另外一个外设负责更新。如果开启数据cache,那么CPU总是从cache中读取数据,这时,cache中的数据和内存中的数据出现不一致,程序执行出现逻辑错误。注意,此时CPU是使用LDR访存指令来访问内存,但是仍然没有得到正确的内存数据。即使使用volatile关键字也无济于事,因为volatile是在指令级上影响C语言到汇编语言的关键字,但是CPU在访问内存时,仍然需要经过cache的缓冲。

在开数据cache的情况下,可以将特定的内存地址设置为不使用cache,以确保CPU访问的是内存。具体就是页表项的Cache属性。

个人理解:

cache是夹在CPU和内存之间的一个缓存,当cache开启时,CPU从内存读写数据时会经过cache,并且留下备份,当涉及到有DMA等外设也会从内存读取数据时,容易产生错误,这时候需要手动去invalidate或者flush缓存(cache)。

常见的有三种情况:

第一种:写操作

当我们配置某些参数或者数据需要最终写到外设上时,CPU会先把他们写到cache上,假如有时序要求,cache没有马上刷新,这时候就存在你的内存和cache之间数据不一致的情况,这时候DMA去内存就拿到了错误的数据,正确的做法是在设置参数或者写入数据时,手动的flush一下cache,可以是全部或者几行,具体视情况而定,保证内存和cache之间数据的一致性。

第二种:读操作

当外设写某些数据到我们的内存上,我们需要读入时,数据也会经过cache,尤其是对相同寄存器的多次配置,或有值的变化时,很容易产生cache和内存数据不一致的情况,正确的方法是在读数据之前invalidate cache然后进行读操作,这时候数据不经过cache,保证是正确的数据。

第三种:写/读操作

当有读写混合的操作时,应该invalidate和flush联合起来使用,具体的方式需要视程序的情况而定,cache的一致性问题有时候很隐蔽,通过常规的gdb或者Jlink都是看到的经过cache的值(cache打开的情况下),不容易debug问题所在。无视程序性能的情况下,可以考虑关闭cache

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐