使用ZYNQ或者MPSoC的好处是可以通过PL逻辑设计硬件加速器,对功能进行硬件加速。加速器和ARM之间的交互信息一般包含自定义加速指令传递、待计算数据以及计算结果。这三种交互信息为了实现高性能往往需要使用DMA进行通信。考虑两种场景,一种是ARM将运算数据的结果或者一些待处理数据发往PL侧加速,一种是PL处理结果DMA传输到DDR内存,ARM读取数据接着处理。当有操作系统管理内存时,这些操作没什么问题,PL看到的DDR数据总是更新过的;ARM接收到DMA中断后,读取内存数据,总能读取到更新后的数据。但如果在SDK环境下裸调,由于Dcache的存在,CPU和PL往往发现看到的是部份更新的数据,因此需要在代码中加入对Dcache的处理。主要有两种方法
第一种方法:
简单粗暴,直接将cache关闭,这时不使用cache,无论是CPU还是PL都是直接更新DDR中的数据,所以不存在Cache一致性的问题。
初始化的时候加入以下以下语句:
#include "xil_cache.h"
Xil_DCacheDisable();
Xil_ICacheDisable();
但这种方法有缺陷,由于Cache关闭,CPU计算的中间结果保存在DDR内存中,每次取数据从DDR取,而不是Cache,因此性能会急剧下降。
第二种方法:
CPU传数据给外设,CPU写完数据后,其实数据存在Dcache中,这时需要cache中关联地址的cacheline刷新到内存中:
Xil_DCacheFlushRange(DDR_BASE,Length);//里面参数分别放传输数据的DDR地址,数据长度(字节为单位)
PL外设传输数据给CPU,CPU接着处理。由于CPU首先从Dcache中查看是否有DDR的关联内容,如果有的话,直接从cache取,这就带来问题,因为最新数据在DDR中,而不在cache中,因此正确的做法是在DMA传送开始前,将cache中与该DDR地址的关联部份做一次invalid,这样CPU读取时发现cache没有关联的cacheline,就会直接从DDR取数据,从而保证获取的数是更新过的。
因此在把DMA地址告诉PL外设,在启动传输前,做一次cacheline的invalid操作:
Xil_DCacheInvalidateRange(IMAGES_OUT_BASE & 0xffffffc0,(length/cache_line_size+1)*cache_line_size); //注意地址必须cacheline对齐。长度也必须cache line对齐。
其中coretex-A9的cache_line_size是32字节, A53是64字节。
总结:在SDK裸跑环境下要保持cache一致性,要不关闭cache,降低性能;要不代码中需要在合适的地方插入cache flush或者invalid语句。
原则是:CPU批量写的数据,需要更新到DDR中给其他设备使用,做Dcacheflush,保证其他设备看到最新数据。CPU想要直接用DDR中的数据,做InvalidDcacheFlush,保证cache中是被外设更新过的数据,invalid时注意cache line对齐。
|