1.简介
hifi3是一个配置很低的dsp芯片,对于一些低效算法,是可以胜任的。目前拿到手的芯片频率在160MHZ,拥有5级流水线,32bit/16bit乘法,两片Local SRAM,128KB+64KB,小端字节序。
做软件程序,一种思路是开源,一种是闭源。开源的好处是需要看代码,做修补工作,闭源的好处是提供api,只需要熟悉api即可。可以把该dsp看作软件上的开源,所以必须需要看里面的设计实现。若想熟悉该芯片,则在硬件上,需要看芯片架构、位宽、存储模式、大小端格式等,软件上则需要看数据类型、数据类型长度大小、数据表达形式等。
对应到商业模式上,则需要人力一次性投入,然后批量卖产品。相对而言,则是专业性产品,软件尽量提供lib库(api接口),这种对于人力开销相对较小,那么库的实现则需简单并且稳定可靠。
2.数据类型
hifi3 dsp芯片拥有自有的数据类型,但是可以使用C编程,通过"强制"转换到自有类型。其拥有自己的一套语法结构,我也单独配套示例程序说明一下。
1.ae_int32x2
由两个32位的数据类型组成一个64位的数据,8字节对齐。如果使用int类型强制转换,那么在内存分布上,将是两个相同的32位数据,举例如下:
void test()
{
int mem1 = 0x1234;
int mem2 = 0x5678;
ae_int32x2 p = mem1;
int *p1 = (int *)&p;
printf("(int)p = 0x%0x\n",(int)p);
printf("*(p1) = 0x%0x\n",*(p1));
printf("*(p1+1) = 0x%0x\n",*(p1+1));
ae_int32x2 p2 = AE_MOVDA32X2(mem1, mem2);
int *p3 = (int *)&p2;
printf("p3=0x%0x,*(p3) = 0x%0x\n",p3,*(p3));
printf("p3+1=0x%0x,*(p3+1) = 0x%0x\n",p3+1,*(p3+1));
}
输出值为:
(int)p = 0x1234
*(p1) = 0x1234
*(p1+1) = 0x1234
p3=0x2ffffeb8,*(p3) = 0x1234
p3+1=0x2ffffebc,*(p3+1) = 0x5678
分析:
AE_MOVDA32X2作用是将两个32位数拼成一个64位数。
2.ae_int64
举一个例子:
void test()
{
int a = 0x40000000;
ae_int64 q = AE_CVTQ56A32S(a);
int *p = (int *)&q;
printf("p=0x%0x,*p=0x%0x\n",p,*p);
printf("p+1=0x%0x,*(p+1)=0x%0x\n",p+1,*(p+1));
}
结果为:
p=0x2ffffeb8,*p=0x0
p+1=0x2ffffebc,*(p+1)=0x400000
分析:
AE_CVTQ56A32S()是将32位有符号数按照Q31存储,然后转换成Q56形式。
3.ae_f64
举一个例子:
void test()
{
int mem1 = 0x40000000;
int mem2 = 0x12300000;
ae_int32x2 p = AE_MOVDA32X2(mem1, mem2);/*mem2-high,mem1-low*/
ae_f64 q = AE_CVTQ56P32S_L(p);
int *pointer = (int *)&q;
printf("*pointer=0x%0x\n",*pointer);
printf("*(pointer+1)=0x%0x\n",*(pointer+1));
q = AE_CVTQ56P32S_H(p);
pointer = (int *)&q;
printf("*pointer=0x%0x\n",*pointer);
printf("*(pointer+1)=0x%0x\n",*(pointer+1));
}
先看结果:
*pointer=0x0
*(pointer+1)=0x123000
*pointer=0x0
*(pointer+1)=0x400000
可以发现,使用的AE_MOVDA32X2()与前面的效果刚好相反。也就是高位变为地位,低位变为高位,按照文档介绍,这种解释是正确的。我也没明白为什么前面的实验结果不是这样的。另外说一句,这就是这种开放式的芯片的弊端,很多东西实验完之后有点莫名其妙。
再多解释一下:
AE_CVTQ56P32S_L()是取p的低32位,因为p本来是一个64位的数,所以取出来其实是mem2的数,之后会将其变为Q56的数。
4. AE_TRUNCI32X2F64S
举一个例子:
void test()
{
ae_int64 mem1 = 0x4000000000000;/*0x4 0000 0000 0000*/
ae_int64 mem2 = 0x123000000000;/*0x1230 0000 0000*/
ae_int32x2 des = AE_TRUNCI32X2F64S(mem1, mem2, 8);
int *p = &des;
printf("*p=0x%0x\n",*p);
printf("*(p+1)=0x%0x\n",*(p+1));
}
结果为:
*p=0x4000000
*(p+1)=0x123000
分析:
这个函数功能是将mem1,mem2先左移8位,然后将高32位存储起来。
5.AE_SAT24S
举一个例子:
void test()
{
int mem1 = 0x40400000;
int mem2 = 0x00010000;
ae_int32x2 d = AE_MOVDA32X2(mem1, mem2);
ae_f24x2 des = AE_SAT24S(d);
int *p = (int *)&des;
printf("*p=0x%0x\n",*p);
printf("*(p+1)=0x%0x\n",*(p+1));
}
结果为:
*p=0x7fffff00
*(p+1)=0x1000000
分析:
首先,还是回到AE_MOVDA32X2(),可以看到这次实验,仍然是mem1为低位,还是我也不知道原因。继续分析,AE_SAT24S()能辨识一个数是否超过24位表达范围。比如mem1超过了后面的表达范围,所以直接将值设置为了最大值,注意,"浮点"表示占用了高32位,所以能看到后8位为0。
6.AE_MOVINT24X2_FROMF64
举例如下:
void test()
{
int mem1 = 0x40000000;
int mem2 = 0x12300000;
ae_int32x2 p = AE_MOVDA32X2(mem1, mem2);/*mem2-high,mem1-low*/
ae_f64 q = AE_CVTQ56P32S_L(p);
int *pointer = (int *)&q;
printf("*pointer=0x%0x\n",*pointer);
printf("*(pointer+1)=0x%0x\n",*(pointer+1));
/*get the value to two 24 bit*/
ae_int24x2 val_24 = AE_MOVINT24X2_FROMF64(q);
pointer = (int *)&val_24;
printf("*pointer=0x%0x\n",*pointer);
printf("*(pointer+1)=0x%0x\n",*(pointer+1));
}
结果为:
*pointer=0x0
*(pointer+1)=0x123000
*pointer=0x123000
*(pointer+1)=0x0
*pointer=0x0
*(pointer+1)=0x400000
分析:
其实 AE_MOVINT24X2_FROMF64就是将一个64位数拆分为2个24位数。
7.其他一些功能介绍
a.AE_MULFP32X2RAS()能达到两个Q31想乘不会超出值。注意可以一次计算1对高低位相乘。
3.数据表达形式
可以使用浮点数的加、减、乘等运算查看数据的精度以及耗时情况。做为一份图表,可以用来查看。
再从hifi3(160MHZ)与tms320c6748(456MHZ)对比几个数学函数:
函数 | hifi3耗时 | tms320c6748耗时 |
---|---|---|
sin | 0.0375us(应该是优化掉了) | 1.87us |
sinh | 10.5us | 2.7us |