一个跟Cache有关的通信应用异常话题
曾经有STM32用户反馈如下情况:
选择STM32F7芯片开发产品,使用了USART1的DMA接收和发送,并开启了USART1的IDLE中断,IDLE中断正常,能成功接收到数据。通过仿真能看到接收缓冲区数组中的数据,但就是不能访问它。如果不访问,重新开启DMA接收能正常接收,并且在仿真状态下也能查看。只要访问一次这个接收数组,下一次开启DMA接收就不能接收数据了。
客户还补充说,之前使用STM32F1芯片时实现同样功能没有任何问题。
从客户的反馈来看,他是说不能对接收到的数据进行读取访问,读取之后就再也不能接收后续数据了。这样说听起来有点玄乎。
随便找块STM32F7的nucleo板进行验证测试,反复验证确认,没有发生客户所说的现象。我在程序代码中也加了对接收数组数据的读取操作。使用循环模式不停让DMA进行UART的收、发,并不时修改发送缓冲区的数据,接收端一直能正常接收,收、发端数据完全一致。【如下图所示】
既然不存在客户反馈的读取接收数组后就无法继续接收数据的问题。那客户的真实问题到底是什么呢?要表达什么呢?后来进一步得知,客户的代码中有如下语句,它会读取接收缓冲的数据。
客户补充说,程序不能执行上面代码,否则就不能再接收后续数据了。屏蔽掉这条语句在仿真状态下能观察到aRxBuffer接收正常。
现在事实上并非他所表述的那样,他真正要表达的是自从读取该接收数组数据之后,不管后续的收发数据怎么变化,他从这个函数里都没法从接收缓冲区读到新数据并显示出来。
我在测试代码里也写了类似代码,顺便瞧瞧结果。我在测试代码了建了个临时观察数组temp[],从接收数组aRxBuffer里读取几个数据存放进去。
测试中可以发现,无论怎样修改发送缓冲区aTxBuffer的数据,接收数组aRxBuffer的数据都会同步变化,但这个temp[]数组里的数据永远都保持第一次发送/接收到的数据。
看到这里,结合测试现象大致得知原因了,问题应该跟D-cache数据与实际存储区数据一致性问题有关。
STM32F7系列芯片支持高速I-Cache和D-CACHE,接收数组aRxBuffer内存区数据默认可以使用D-cache,使用它可以大大提高CPU的访问速率。不过,使用D-Cache要注意实际物理存储区与高速缓存区的数据一致性问题。
具体结合本案例,发送缓冲区的数据通过UART发送出去后,UART数据寄存器接受到数据后,DMA将数据写入接收缓冲区RxBuffer[],CPU从RxBuffer[]将数据读进Temp[]数组。
在使能D-Cache的前提下,CPU第一次读取接收数组的数据到Temp[]数组后,并将相应数据放入D-cache进行缓存。之后,CPU再来读接收数组的数据时,并没有前往接收数组实际物理存储区去读取数据,而是直接去D-cache里读取数据了。由于Cache数据没有做更新,这样的话,即使接收数组Rxbuffer数据变化了,D-Cache里的数据并未相应改变,所以就出现了CPU从Cache里读到的数据总不变的情况,给人一种后续数据没有接收进来的错觉。
那如何解决这个问题呢?方法较多,可以根据具体应用情况而定。这里介绍两种方法:
1、每次读取接收数组的数据之前先对D-cache做个清除操作或者说刷新操作,让实际物理存储器里的数据与Cache里的数据先做同步更新。相应的STM32库函数代码为:SCB_CleanDCache ( );
2、在读取接收数组的数据之前先做个D-cache失效操作,这样让CPU无视D-Cache而直接去实际物理存储器区读取数据。相应的库函数代码是SCB_InvalidateDCache();
经过验证测试,两种办法都可行,也不再出现客户反馈的异常现象。
关于带D-Cache的多主存储访问过程中的数据一致性问题,既可能发生在CPU对某可缓存存储区进行写操作,DMA对相应存储区进行读操作时,也可能发生在DMA对某可缓存存储区进行写操作,CPU对相应区域进行读操作的情形。本案例就是后面第二种情形,在该情形下,为了保证SRAM与D-Cache里数据的一致性,在CPU对SRAM区数据读操作之前,须针对D-Cache进行失效操作。
关于STM32 D-Cache及数据一致性问题,已经不少文章介绍,这里不再赘述。在此分享该实际案例,一方面是为了加深相关知识的了解,另一方面是为了多引起应用工程师对相关问题的关注。毕竟因D-Cache一致性导致问题时也是相对比较隐蔽的,加上以往低端MCU也没有这类东西,缺乏经验积累,应用中容易被忽视而找不到原因。
最后,推荐一篇关于STM32F7 Cache相关话题的应用笔记--AN4839,可去ST官网搜索下载阅读之。
=======================================
往期话题链接: