Cache引发的数据一致性问题

背景

调试MCU(S32K312)和SOC之间的串口时,发现逻辑分析仪抓到的数据波形异常。

从逻辑分析仪的波形来看,SOC是前八个字节,后八个字节这样去发的。但是仿真查看MCU接收的数据,收到的要不就是全是前八个字节,要不就是后八个字节。数据接收不完整,所以ACK发不出去, ACK回复超时SOC会通知MCU Reset串口,如此循环…

仿真

首先,怀疑是不是中断没有进入,但是增加了count去记录中断进入的次数,发现中断是正常进入的。考虑到SOC会发很多东西,而且收不到ACK会重发,所以让SOC先发一帧,这样调试起来比较清晰。

SOC端改为只发送一帧数据后,很明显的现象就是,明明逻辑分析仪抓到的是非0数据。但是,有时候仿真看收到的是全0数据;明明代码给发送的buffer赋值了,仿真也跑了赋值,但是实际发出去的是全0。从这个现象可以看出,这个问题是Cache的问题,开启Cache的RAM,CPU和DMA对RAM多主访问的情况,会有数据一致性问题。

Cache介绍:Cache扫盲

代码分析

之前bss段是链接到DTCM的,MPU配置是non_cacheable的,所以之前用DMA的时候没有问题。后来,由于内存不足,UART相关的代码,链接到了SRAM,但是SRAM在启动代码里面的SystemInit函数配置MPU时,全部配置成了Cache,并且是写回策略,导致引发了数据一致性问题。

解决方案

解决该问题的方法有两种:

  1. 手动更新Cache,在合适的时机清理或无效Cache。
  2. 将内存属性设置为non-cacheable的。

手动更新Cache

手动更新Cache的接口函数在core_cm7.h文件可以找到。

Cache函数的参数类似,介绍如下:

  • addr:要清除内存的起始地址。地址要求32字节对齐,否则自动强制按照D-cache的行大小对齐。

  • dsize:清除内存大小,通常要求是32的整数倍,不是整数倍需要注意正确的操作顺序。

图中高亮的DCIMVAC寄存器是一个ARM处理器中的寄存器,它用于执行无效目标地址操作。如果数据在集群内是脏的,则在无效之前执行清除操作。

也可以使用RTD Cache_Ip.c里面的接口函数。

由于参数要求32字节对齐,代码里面UART的数据结构体是没对齐的,需要在DMA使用到的接收buffer后面填充32个字节。不然,会把后面的Cache也更新了。因为从前面函数的定义可以看到,Cache的操作是连续的,每次操作32个字节,如果地址没有32字节对齐且buffer大小不是32的倍数,后面又定义了有效的变量,在执行invalid操作时我们并不希望invalid该变量,但由于invalid的操作是连续的,会导致该变量的值被覆盖。所以,需要对DMA接收的buffer做填充处理,发送的buffer可以不用处理。

然后,在使用DMA发送数据前或者读取DMA收到的数据前,清理或无效化Cache就好了。

使用这种方法可以继续将UART变量链接到SRAM,不用改链接脚本,不用自定义段,也不用改MPU的属性。

修改内存属性

将内存设置为non-cacheable的解决方法有两种:

  1. 修改链接脚本,把int_sram section1的空间改小,把之前的non-cacheable空间改大。由于MPU区域的大小必须是2的次方大小,起始地址必须要是域大小的整数倍。这种方法改动量比较大,而且之前SRAM已经用了一些空间,很难凑出合适的数据,所以不采用这种方法。

  2. 把链接的段属性改成non-cacheable

    在链接脚本中增加自定义段:

    把UART相关的变量链接到自定义段:

    自定义段的地址处于int_sram section2的区域,再将section2配置为non_cacheable属性即可。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2025 wrd
  • 访问人数: | 浏览次数:

      请我喝杯咖啡吧~

      支付宝
      微信