AI 引擎通常支持多通道 MAC 运算。对于 FIR 应用的变体,可使用 多通道乘法 - sliding_mul 中引入的 aie::sliding_mul*
类和函数组合。
在本节中,您将选择
aie::sliding_mul
和 aie::sliding_mac
函数,搭配通道数 Lanes=8
且点数 Points=8
。数据和系数阶跃大小均为 1,这是默认值。例如,acc =
aie::sliding_mac<8,8>(acc,coe[1],0,buff,8);
会执行:Lane 0: acc[0]=acc[0]+coe[1][0]*buff[8]+coe[1][1]*buff[9]+...+coe[1][7]*buff[15];
Lane 1: acc[1]=acc[1]+coe[1][0]*buff[9]+coe[1][1]*buff[10]+...+coe[1][7]*buff[16];
...
Lane 7: acc[7]=acc[7]+coe[1][0]*buff[15]+coe[1][1]*buff[16]+...+coe[1][7]*buff[22];
请注意,buff
数据从不同通道内的不同索引开始。它需要准备好超过 8 个样本(从 buff[8]
到 buff[22]
)才能执行。
由于它有 32 个抽头,因此 FIR 需要执行一次 aie::sliding_mul<8,8>
运算和三次 aie::sliding_mac<8,8>
运算才能计算 8 条输出通道。数据缓冲器由串流端口通过 buff.insert
来更新。
矢量化内核代码如下所示:
//keep margin data between different executions of graph
static aie::vector<cint16,32> delay_line;
alignas(aie::vector_decl_align) static cint16 eq_coef[32]={{1,2},{3,4},...};
__attribute__((noinline)) void fir_32tap_vector(input_stream<cint16> * sig_in, output_stream<cint16> * sig_out){
const int LSIZE=(SAMPLES/32);
aie::accum<cacc48,8> acc;
const aie::vector<cint16,8> coe[4] = {aie::load_v<8>(eq_coef),aie::load_v<8>(eq_coef+8),aie::load_v<8>(eq_coef+16),aie::load_v<8>(eq_coef+24)};
aie::vector<cint16,32> buff=delay_line;
for(int i=0;i<LSIZE;i++){
//1st 8 samples
acc = aie::sliding_mul<8,8>(coe[0],0,buff,0);
acc = aie::sliding_mac<8,8>(acc,coe[1],0,buff,8);
buff.insert(0,readincr_v<4>(sig_in));
buff.insert(1,readincr_v<4>(sig_in));
acc = aie::sliding_mac<8,8>(acc,coe[2],0,buff,16);
acc = aie::sliding_mac<8,8>(acc,coe[3],0,buff,24);
writeincr(sig_out,acc.to_vector<cint16>(SHIFT));
//2nd 8 samples
acc = aie::sliding_mul<8,8>(coe[0],0,buff,8);
acc = aie::sliding_mac<8,8>(acc,coe[1],0,buff,16);
buff.insert(2,readincr_v<4>(sig_in));
buff.insert(3,readincr_v<4>(sig_in));
acc = aie::sliding_mac<8,8>(acc,coe[2],0,buff,24);
acc = aie::sliding_mac<8,8>(acc,coe[3],0,buff,0);
writeincr(sig_out,acc.to_vector<cint16>(SHIFT));
//3rd 8 samples
acc = aie::sliding_mul<8,8>(coe[0],0,buff,16);
acc = aie::sliding_mac<8,8>(acc,coe[1],0,buff,24);
buff.insert(4,readincr_v<4>(sig_in));
buff.insert(5,readincr_v<4>(sig_in));
acc = aie::sliding_mac<8,8>(acc,coe[2],0,buff,0);
acc = aie::sliding_mac<8,8>(acc,coe[3],0,buff,8);
writeincr(sig_out,acc.to_vector<cint16>(SHIFT));
//4th 8 samples
acc = aie::sliding_mul<8,8>(coe[0],0,buff,24);
acc = aie::sliding_mac<8,8>(acc,coe[1],0,buff,0);
buff.insert(6,readincr_v<4>(sig_in));
buff.insert(7,readincr_v<4>(sig_in));
acc = aie::sliding_mac<8,8>(acc,coe[2],0,buff,8);
acc = aie::sliding_mac<8,8>(acc,coe[3],0,buff,16);
writeincr(sig_out,acc.to_vector<cint16>(SHIFT));
}
delay_line=buff;
}
void fir_32tap_vector_init()
{
//initialize data
for (int i=0;i<8;i++){
aie::vector<int16,8> tmp=get_wss(0);
delay_line.insert(i,tmp.cast_to<cint16>());
}
};
注释:
-
alignas(aie::vector_decl_align)
可用于确保数据已对齐,可执行矢量加载和存储。 - 每个主循环迭代都会计算多个样本。因此可以减少循环计数。
- 数据更新、计算和数据写入在代码中交织执行。如需判定要读取哪一部分的数据缓冲器
buff
,可使用aie::sliding_mul
的data_start
来加以控制。 - 如需了解有关
aie::sliding_mul
支持的数据类型和通道数量的更多信息,请参阅 AI 引擎 API 用户指南(UG1529)。
主循环的启动时间间隔应明确。要查找循环的启动时间间隔,请执行以下操作:
- 将
-v
选项添加到aiecompiler
以输出内核编译的详细报告。 - 打开内核编译 log 日志文件,例如,
Work/aie/<COL_ROW>/<COL_ROW>.log
。 - 在此日志中,搜索关键字(例如,
do-loop
)以查找循环的启动时间间隔。示例如下:其中:HW do-loop #2821 in ".../fir_32tap_vector.cc", line 21: (loop #3) : critical cycle of length 130 : ... minimum length due to resources: 128 scheduling HW do-loop #2821 (algo 2) -> # cycles: ...... NOTE: automatically decreased the number of used priority functions to 3 to reduce runtime -> # cycles: .....183 (exceeds -k 110) -> no folding: 183 -> HW do-loop #2821 in ".../Vitis/2023.2/aietools/include/adf/stream/me/stream_utils.h", line 870: (loop #3) : 183 cycles
- 循环的启动时间间隔为 183。这意味着约在 183/32~=6 个周期内生成一个样本。
-
(exceeds -k 110) -> no folding
消息表明,调度器当前并未尝试软件流水打拍,因为循环周期计数超出限值。
- 要改写循环周期限值,请将用户约束(例如,
--Xchess="fir_32tap_vector:backend.mist2.maxfoldk=200"
)添加到aiecompiler
。由此产生的结果示例如下:
scheduling HW do-loop #2821 (algo 2) -> # cycles: ...... NOTE: automatically decreased the number of used priority functions to 3 to reduce runtime -> # cycles: .....183 (modulo) -> # cycles: ... ok (required budget ratio: 2) ... (resume algo) -> after folding: 161 (folded over 1 iterations) -> HW do-loop #2821 in ".../Vitis/2023.2/aietools/include/adf/stream/me/stream_utils.h", line 870: (loop #3) : 161 cycles
其中,软件生成每个样本大约需要 161/32~=5 个周期。