以下显示了该矩阵乘法的标量参考代码示例。请注意,数据存储在列中。
void matmul_mat8_scalar(input_window_int16* matA,
input_window_int16* matB,
output_window_int16* matC){
for(int i=0; i<M; i++){//M=64
for(int j=0;j<L;j++){//L=2
int temp = 0 ;
for(int k=0; k<N; k++){//N=8
temp += window_read(matA)*window_readincr(matB);//B is circular buffer, size N*L
window_incr(matA,64); //Jump of 64 elements to access the next element of the same row
}
window_write(matC,(int16_t)(temp>>15)) ;
window_incr(matC,64); //Jump to the next column
}
window_incr(matA,1); //Jump of one element for moving to the next row.
window_incr(matC,1); //Jump to the next row
}
}
正如前述示例中所分析的,mac16
内部函数是组合计算 16 条通道的最佳选择,因为可以同时加载来自一个列的 16 个 int16。要计算任一列中的 16 个输出数据,需要 4 次 mac16
运算。矢量“a”中的相同数据将使用两次,以便为两个输出列计算数据。因此,可以加载 2 列数据,并且有 2 项 mac16
用于累加至 2 个输出列。这 2 次加载和 2 项 MAC 会重复 4 次,以获取 2 个输出列的结果。以下伪代码中显示了此方法。
C_[0:15,0] = A_[0:15,0:1]*B_[0:1,0]
C_[0:15,1] = A_[0:15,0:1]*B_[0:1,1]
C_[0:15,0]+= A_[0:15,2:3]*B_[2:3,0]
C_[0:15,1]+= A_[0:15,2:3]*B_[2:3,1]
C_[0:15,0]+= A_[0:15,4:5]*B_[4:5,0]
C_[0:15,1]+= A_[0:15,4:5]*B_[4:5,1]
C_[0:15,0]+= A_[0:15,6:7]*B_[6:7,0]
C_[0:15,1]+= A_[0:15,6:7]*B_[6:7,1]
在先前代码中,每个“*”都表示一项 MAC 运算。C_[0:15,0]
和 C_[0:15,1]
表示单独累加的 2 个输出列。A_[0:15,0:1]
表示列 0 和 1,每个列都有 16 个元素。B_[0:1,0]
表示含 2 个元素的列 0。在真正矢量化的代码中,将会有一个代码循环,因为有 64 个输出行。要使用的 mac16
内部函数具有如下接口。
v16acc48 mac16 ( v16acc48 acc,
v64int16 xbuff,
int xstart,
unsigned int xoffsets,
unsigned int xoffsets_hi,
unsigned int xsquare,
v16int16 zbuff,
int zstart,
unsigned int zoffsets,
unsigned int zoffsets_hi,
int zstep
)
缓冲器包含参数(起始、偏移、平方和阶跃)用于计算到缓冲器内的索引(矢量寄存器)。如需了解有关利用这些参数进行通道寻址的方案的详细信息,请参阅 MAC 内部函数。
请注意,mac16
内部函数原型不同于先前矩阵矢量乘法示例中介绍的原型。此处的 xbuff
是 v64int16
,允许 2 组数据以交织方式来存储和使用。
下一节中介绍了如何利用 MAC 内部函数进行编码。