一、引言 f
TI TMS320C641X DSP是德州仪器最高端的数字信号处理器,性能卓越,但因为其价格相对较高,只能针对特定行业的高端用户,对于大部分DSP研发人员,有时甚至很难接触到它们,所以相关深入的讨论很少。而媒体处理器TI TMS320DM642/DM640/DM643出现了,它集成了丰富的外设,甚至可以说,对于音视频和基于网络的应用比C641X更易使用,价格却十分便宜,所以在各种领域已开始大规模应用,当然关键的仍然是它们有着与C641X一样TI最强大的内核C64x,所以希望高效地(如更高分辨率或更多路)使用DM64x,除了片上内存的有效利用外,程序上基于C64x指令集(VelociTI.2 Core)的调度汇编级的优化变得十分关键。
TMS320C64x指令集与C6000公共指令集(VelociTI,支持流水线、VLIW,如TMS320C62x所使用的)相比共扩展了88条指令,这些指令的扩展是建立在其CPU结构改进基础上的,主要分三种情况:
(1)与C6000公共指令集的指令功能及运行完全一致,只是增加了可执行的功能单元。这类指令共有6条。
(2)与C6000公共指令集内对应指令的功能及运行基本一致,主要差别是指令操作数的类型增加了,即大量引入了SIMD(单一指令多数据流扩展)指令。这类指令有34条。
(3)新增指令48条。
原来基于C62x(VelociTI)的优化程序可以不做更改的运行在C64x,但如果能够利用这些扩展的指令,情况将如何呢?本文将通过实例详细说明如何应用C64x系列指令进行程序优化,以达到提高执行效率的目的。这里我们以mpeg-4编解码中的一个函数为例,具体说明如何应用C64x系列指令集进行调度汇编级的程序优化。
二、应用C6000公共指令集的优化程序
在这里,将要进行优化的C语言函数为:
void interpolate8x8_halfpel_h_c(uint8_t * dst,
const uint8_t* src,
const uint32_t DstStride,
const uint32_t Srcstride,
const uint32_t rounding)
{
uint32_t i, j;
for (j = 0; j < 8; j++) {
for (i = 0; i < 8; i++) {
int32_t tot =(int32_t) src[j * Srcstride + i] + (int32_t) src[j * Srcstride + i + 1];
tot = (tot + 1 - rounding) >> 1;
dst[j * DstStride + i] = (uint8_t) tot;
} } }
上面函数的执行体为一嵌套循环,函数的源操作数和目的操作数都是字节型数据。为了对比说明C64x系列指令集的优势,我们首先用C6000系列的公共指令集对该函数的循环体进行汇编优化,其内核程序(因为篇幅原因,流水线填充和排空部分不另外列出,可参考内核导出)如下:
H_LOOP: ;循环内核
add .l1 A_src1,A_src2,A_dst1 ;1
|| add .l2 B_src5,B_src6,B_dst5
|| ldbu .d1t1 *+A_ptrSrc[4],A_src5
|| ldbu .d2t2 *+B_ptrSrc[4],B_src9
|| sub .s1 Acnt,1,Acnt
add .s1 A_dst1,A_round,A_dst1 ;2
|| add .s2 B_dst5,B_round,B_dst5
|| add .l1 A_src2,A_src3,A_dst2
|| add .l2 B_src6,B_src7,B_dst6
|| ldbu .d1t1 *++A_ptrSrc[A_SrcStride],A_src1
|| ldbu .d2t2 *++B_ptrSrc[B_SrcStride],B_src5
shru .s1 A_dst1,1,A_dst1 ;3
|| shru .s2 B_dst5,1,B_dst5
|| add .l1 A_dst2,A_round,A_dst2
|| add .l2 B_dst6,B_round,B_dst6
|| ldbu .d1t1 *+A_ptrSrc[1],A_src2
|| ldbu .d2t2 *+B_ptrSrc[1],B_src6
add .l1 A_src3,A_src4,A_dst3 ;4
|| add .l2 B_src7,B_src8,B_dst7
|| shru .s1 A_dst2,1,A_dst2
|| shru .s2 B_dst6,1,B_dst6
|| stb .d1t1 A_dst1,*A_ptrDst++[1]
|| stb .d2t2 B_dst5,*B_ptrDst++[1]
[A1]B .s2 H_LOOP ;5
|| add .l1 A_dst3,A_round,A_dst3
|| add .l2 B_dst7,B_round,B_dst7
|| stb .d1t1 A_dst2,*A_ptrDst++[1]
|| stb .d2t2 B_dst6,*B_ptrDst++[1]
add .l1 A_src4,A_src5,A_dst4 ;6
|| add .l2 B_src8,B_src9,B_dst8
|| shru .s1 A_dst3,1,A_dst3
|| shru .s2 B_dst7,1,B_dst7
add .s1 A_dst4,A_round,A_dst4 ;7
|| add .s2 B_dst8,B_round,B_dst8
|| ldbu .d1t1 *+A_ptrSrc[2],A_src3
|| ldbu .d2t2 *+B_ptrSrc[2],B_src7
shru .s1 A_dst4,1,A_dst4 ;8
|| shru .s2 B_dst8,1,B_dst8
|| stb .d1t1 A_dst3,*A_ptrDst++[1]
|| stb .d2t2 B_dst7,*B_ptrDst++[1]
ldbu .d1t1 *+A_ptrSrc[3],A_src4 ;9
|| ldbu .d2t2 *+B_ptrSrc[3],B_src8
stb .d1t1 A_dst4,*A_ptrDst++[A_DstAdjust] ;10
|| stb .d2t2 B_dst8,*B_ptrDst++[B_DstAdjust]
这个优化程序是应用C6000公共指令集的最佳优化,它将C语言函数循环体的内循环进行了展开,采用分段读取源操作数的方法,即同时以一次内循环中8个数据的第一、第五个字节开始,使用ldbu指令读取数据,这样可以比全部从边侧读取字节数据的方法节省一个内核周期。数据处理完后使用stb指令存储数据。最终该程序的内核长度为10个周期,执行次数为七次,整个函数占用88个指令周期 |