使用单内核的矢量化版本 - 2023.2 简体中文

AI 引擎内核与计算图编程指南 (UG1079)

Document ID
UG1079
Release Date
2023-12-04
Version
2023.2 简体中文

AI 引擎通常支持多通道 MAC 运算。对于 FIR 应用的变体,可使用 多通道乘法 - sliding_mul 中引入的 aie::sliding_mul* 类和函数组合。

在本节中,您将选择 aie::sliding_mulaie::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_muldata_start 来加以控制。
  • 如需了解有关 aie::sliding_mul 支持的数据类型和通道数量的更多信息,请参阅 AI 引擎 API 用户指南(UG1529)

主循环的启动时间间隔应明确。要查找循环的启动时间间隔,请执行以下操作:

  1. -v 选项添加到 aiecompiler 以输出内核编译的详细报告。
  2. 打开内核编译 log 日志文件,例如,Work/aie/<COL_ROW>/<COL_ROW>.log
  3. 在此日志中,搜索关键字(例如,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 消息表明,调度器当前并未尝试软件流水打拍,因为循环周期计数超出限值。
  4. 要改写循环周期限值,请将用户约束(例如,--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 个周期。