使用CLT优化C6000系列DSP程序
1 C6000 DSP内核CACHE机制
当前,高性能DSP内核C66系统的存储器框图如图所示。
存储器被分成了三级:第一级是L1,包含数据存储器(L1D)和代码存储器(L1P);第二级是代码和数据共用存储器(L2以及MSMC SRAM);第三级是外部存储器,主要是DDR3存储器。L1P、L1D和L2的CACHE功能分别由相应的L1P控制器、L1D控制器和L2控制器完成。
通常我们会把L1P配置成CACHE,而L1P的空间是有限的(32KB),当CPU发出取指命令,首先会从L1P里查找,如果L1P找不到,则到下一级cache或者memory里查找,依次类推,当找到需要的地址,则将其填入L1P里,CPU从中读取执行,若L1P当前位置已有内容,则需要将其置成无效后填入新的数据。
如果代码在内存排布不合理时,可能会发生反复的从L1P里置换出旧的CACHE内容,填入新的CACHE内容,如下图中的极端情况
在该例中A函数中FOR循环反复调用B函数,而A,B,C三个函数在内存地址的分布上,与32KB 边界的偏移地址是一样的,因此,A,B,C将映射L1P里同一个CACHE位置;当执行A时,CPU需要把A函数调入到L1P中,当执行到B时,则需要将L1P中A函数位置无效,调入B,当B返回时,又需要将B无效,调入C函数,C函数返回时再调入B函数……这样反复的对CACHE操作将大大的降低程序的执行效率。
那么,我们应该如何解决该问题?最好的解决方法则是将A, B, C在内存中连续排放,这样,A,B,C反复调用时,对CACHE的操作次数将降到最低,能够有效的提高执行效率。
2 内存优化工具 Cache Layout Tools
通过上述机制可以看到,内存优化主要通过分析函数调用关系和其在内存的分步。由于用户代码日益复杂,人工分析代码调用关系和地址排布需要花费大量的时间。因此,TI提供了一整套内存优化工具 (Cache Layout Tools, 简称 CLT) 来帮助用户轻松快捷地解决该问题。
该工具的原理是在用户进行程序编译时打开生成分析信息选项,编译器会自动加入分析记录代码到用户程序里,而后用户在TI DSP simulator或者DSP芯片上运行该可执行文件,内置的分析代码会自动记录用户的函数调用关系及调用次数。运行的案例越多,则记录的信息会更详细,优化的效果也就越好。
在得到函数运行时信息以后,就可以使用编译器工具对其进行分析,生成函数排布的顺序,最后将此排布顺序输入到编译器里重新编译原代码,生成的可执行文件就已经优化过内存排布,具体的操作可以参照以下实例。
3 实例教程
1. 该实例主要由三个C文件组成,如下
main.c:
• defines main()
• main() calls rare() once
• main() calls main.c:local() 10 times
• defines static local()
• main.c:local() calls lots() 20 times
lots.c:
• defines lots(); globally visible
• lots() calls lots.c:local() 100+ times
• defines lots.c:local()
rare.c:
• defines rare(); globally visible
实例中使用DSP计数器 TSCL来统计cycle数,不失一般性,子函数放在sub目录下。
2. 编译代码
使用TI编译器对该实例进行编译,为了产生用于profile的信息,需要在编译时增加 -- gen_profile_info 选项。在本例中,可在命令行下运行Compile.bat文件,cl6x的具体参数可以参考spru186和spru187两篇文档,一般可以在编译器的安装目录下找到他们,如C:\Program Files (x86)\Texas Instruments\C6000 Code Generation Tools 7.3.9\doc。编译成功完成后,命令行输出如下图
同时在目录下生成OBJ和ASM文件,这个和我们的实验关系不大,可以不用关注。
OUT文件是一会需要下载到芯片里运行的可执行文件,而MAP文件用于帮助我们定位profile信息存放的内存地址。
3. 获取profile信息
打开MAP文件,可以找到.ppdata段的内存地址,这个地址就是profile信息存放处,在例子中
.ppdata 0 0081fecc 00000034 UNINITIALIZED
.ppdata段位于0x0081fecc这个地址,长度是34个byte。
启动CCSv5,连接EVM板,下载OUT文件到DSP上,在main函数末尾加上调试断点,可以让程序到这里停住(实际上,在用户代码中,可以把断点设置在需要的任何地方,profile的信息是实时更新的)。
运行该程序,到达断点后,在View菜单里打开memory browser,将地址设定为0x0081fecc, 可以读到.ppdata的信息,参考以下步骤将其存到工程目录下。
1) 选取save memory
2) 存放路径
3) 确定数据地址和长度,如下图
4. 打开刚才存下的DAT文件,注意到文件头的数据长度是以WORD数目为单位的,我们需要以BYTE数目为单位,如
1651 9 81fecc 0 d 1
修改为
1651 9 81fecc 0 34 1
5. 运行Analysis.bat里的命令,对刚才的运行profile信息进行分析,得到优化后的CMD内存排布文件,该文件内容如下,用户可根据自己的程序进行修改
Generate_pdatfile -le ppdata.dat pprofout.pdat
如果是大端,则将-le选项改为-be选项。
pdd6x pprofout.pdat -eCLTTutoirial.out -o=pprofout.prf
cl6x --abi=coffabi -o2 -mv64+ --include_path="C:\Program Files (x86)\Texas Instruments\C6000 Code Generation Tools 7.3.9/include" --use_profile_info=pprofout.prf --analyze=callgraph main.c .\sub\lots.c .\sub\rare.c
clt6x main.csv lots.csv rare.csv -o pfo.cmd
6. 运行Recompile.bat利用上一步输出的pfo.cmd重新编译输出优化后的OUT文件,cache优化到此完成。
7. 对比前后结果,未优化时cycle数为171843,优化后为161883,在该实例中,利用cache layout tools可以节省约1万cycle。