シングル カーネルを使用したベクター化されたバージョン - 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_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 タップあるので、8 レーンの出力を計算するには、FIR に 1 つの aie::sliding_mul<8,8> 演算と 3 つの aie::sliding_mac<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) を参照してください。

メイン ループの開始間隔 (II) を特定する必要があります。ループの開始間隔を特定するには、次の手順に従います。

  1. aiecompiler-v オプションを追加し、カーネル コンパイルの詳細レポートを出力します。
  2. カーネルのコンパイル ログ (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 サイクルが必要です。